{
 "cells": [
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": ""
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": ""
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": ""
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Python Web 框架\n"
   ]
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "<h1>Table of Contents<span class=\"tocSkip\"></span></h1>\n",
    "<div class=\"toc\"><ul class=\"toc-item\"><li><span><a href=\"#Python-Web-框架\" data-toc-modified-id=\"Python-Web-框架-1\"><span class=\"toc-item-num\">1&nbsp;&nbsp;</span>Python Web 框架</a></span></li><li><span><a href=\"#Django介绍\" data-toc-modified-id=\"Django介绍-2\"><span class=\"toc-item-num\">2&nbsp;&nbsp;</span>Django介绍</a></span><ul class=\"toc-item\"><li><span><a href=\"#简介\" data-toc-modified-id=\"简介-2.1\"><span class=\"toc-item-num\">2.1&nbsp;&nbsp;</span>简介</a></span></li><li><span><a href=\"#特点\" data-toc-modified-id=\"特点-2.2\"><span class=\"toc-item-num\">2.2&nbsp;&nbsp;</span>特点</a></span><ul class=\"toc-item\"><li><span><a href=\"#重量级框架\" data-toc-modified-id=\"重量级框架-2.2.1\"><span class=\"toc-item-num\">2.2.1&nbsp;&nbsp;</span>重量级框架</a></span><ul class=\"toc-item\"><li><span><a href=\"#MVC模式说明\" data-toc-modified-id=\"MVC模式说明-2.2.1.1\"><span class=\"toc-item-num\">2.2.1.1&nbsp;&nbsp;</span>MVC模式说明</a></span></li><li><span><a href=\"#Django的MVT\" data-toc-modified-id=\"Django的MVT-2.2.1.2\"><span class=\"toc-item-num\">2.2.1.2&nbsp;&nbsp;</span>Django的MVT</a></span></li></ul></li></ul></li><li><span><a href=\"#环境安装\" data-toc-modified-id=\"环境安装-2.3\"><span class=\"toc-item-num\">2.3&nbsp;&nbsp;</span>环境安装</a></span></li><li><span><a href=\"#创建第一个Django项目\" data-toc-modified-id=\"创建第一个Django项目-2.4\"><span class=\"toc-item-num\">2.4&nbsp;&nbsp;</span>创建第一个Django项目</a></span><ul class=\"toc-item\"><li><span><a href=\"#创建工程\" data-toc-modified-id=\"创建工程-2.4.1\"><span class=\"toc-item-num\">2.4.1&nbsp;&nbsp;</span>创建工程</a></span><ul class=\"toc-item\"><li><span><a href=\"#创建\" data-toc-modified-id=\"创建-2.4.1.1\"><span class=\"toc-item-num\">2.4.1.1&nbsp;&nbsp;</span>创建</a></span></li><li><span><a href=\"#工程目录说明\" data-toc-modified-id=\"工程目录说明-2.4.1.2\"><span class=\"toc-item-num\">2.4.1.2&nbsp;&nbsp;</span>工程目录说明</a></span></li><li><span><a href=\"#运行开发服务器\" data-toc-modified-id=\"运行开发服务器-2.4.1.3\"><span class=\"toc-item-num\">2.4.1.3&nbsp;&nbsp;</span>运行开发服务器</a></span></li></ul></li><li><span><a href=\"#创建子应用\" data-toc-modified-id=\"创建子应用-2.4.2\"><span class=\"toc-item-num\">2.4.2&nbsp;&nbsp;</span>创建子应用</a></span><ul class=\"toc-item\"><li><span><a href=\"#创建\" data-toc-modified-id=\"创建-2.4.2.1\"><span class=\"toc-item-num\">2.4.2.1&nbsp;&nbsp;</span>创建</a></span></li><li><span><a href=\"#子应用目录说明\" data-toc-modified-id=\"子应用目录说明-2.4.2.2\"><span class=\"toc-item-num\">2.4.2.2&nbsp;&nbsp;</span>子应用目录说明</a></span></li><li><span><a href=\"#注册安装子应用\" data-toc-modified-id=\"注册安装子应用-2.4.2.3\"><span class=\"toc-item-num\">2.4.2.3&nbsp;&nbsp;</span>注册安装子应用</a></span></li></ul></li></ul></li><li><span><a href=\"#模型\" data-toc-modified-id=\"模型-2.5\"><span class=\"toc-item-num\">2.5&nbsp;&nbsp;</span>模型</a></span><ul class=\"toc-item\"><li><span><a href=\"#使用Django进行数据库开发的提示-：\" data-toc-modified-id=\"使用Django进行数据库开发的提示-：-2.5.1\"><span class=\"toc-item-num\">2.5.1&nbsp;&nbsp;</span>使用Django进行数据库开发的提示 ：</a></span></li><li><span><a href=\"#使用Django进行数据库开发的步骤-：\" data-toc-modified-id=\"使用Django进行数据库开发的步骤-：-2.5.2\"><span class=\"toc-item-num\">2.5.2&nbsp;&nbsp;</span>使用Django进行数据库开发的步骤 ：</a></span><ul class=\"toc-item\"><li><span><a href=\"#定义模型类\" data-toc-modified-id=\"定义模型类-2.5.2.1\"><span class=\"toc-item-num\">2.5.2.1&nbsp;&nbsp;</span>定义模型类</a></span></li><li><span><a href=\"#模型迁移-（建表）\" data-toc-modified-id=\"模型迁移-（建表）-2.5.2.2\"><span class=\"toc-item-num\">2.5.2.2&nbsp;&nbsp;</span>模型迁移 （建表）</a></span></li></ul></li></ul></li><li><span><a href=\"#站点管理\" data-toc-modified-id=\"站点管理-2.6\"><span class=\"toc-item-num\">2.6&nbsp;&nbsp;</span>站点管理</a></span><ul class=\"toc-item\"><li><span><a href=\"#管理界面本地化\" data-toc-modified-id=\"管理界面本地化-2.6.1\"><span class=\"toc-item-num\">2.6.1&nbsp;&nbsp;</span>管理界面本地化</a></span></li><li><span><a href=\"#创建管理员\" data-toc-modified-id=\"创建管理员-2.6.2\"><span class=\"toc-item-num\">2.6.2&nbsp;&nbsp;</span>创建管理员</a></span></li><li><span><a href=\"#注册模型类\" data-toc-modified-id=\"注册模型类-2.6.3\"><span class=\"toc-item-num\">2.6.3&nbsp;&nbsp;</span>注册模型类</a></span></li><li><span><a href=\"#发布内容到数据库\" data-toc-modified-id=\"发布内容到数据库-2.6.4\"><span class=\"toc-item-num\">2.6.4&nbsp;&nbsp;</span>发布内容到数据库</a></span></li></ul></li><li><span><a href=\"#视图和URL\" data-toc-modified-id=\"视图和URL-2.7\"><span class=\"toc-item-num\">2.7&nbsp;&nbsp;</span>视图和URL</a></span><ul class=\"toc-item\"><li><span><a href=\"#定义视图\" data-toc-modified-id=\"定义视图-2.7.1\"><span class=\"toc-item-num\">2.7.1&nbsp;&nbsp;</span>定义视图</a></span></li><li><span><a href=\"#配置URLconf\" data-toc-modified-id=\"配置URLconf-2.7.2\"><span class=\"toc-item-num\">2.7.2&nbsp;&nbsp;</span>配置URLconf</a></span></li></ul></li><li><span><a href=\"#模板\" data-toc-modified-id=\"模板-2.8\"><span class=\"toc-item-num\">2.8&nbsp;&nbsp;</span>模板</a></span><ul class=\"toc-item\"><li><span><a href=\"#模板使用步骤\" data-toc-modified-id=\"模板使用步骤-2.8.1\"><span class=\"toc-item-num\">2.8.1&nbsp;&nbsp;</span>模板使用步骤</a></span></li></ul></li><li><span><a href=\"#展示书籍列表\" data-toc-modified-id=\"展示书籍列表-2.9\"><span class=\"toc-item-num\">2.9&nbsp;&nbsp;</span>展示书籍列表</a></span><ul class=\"toc-item\"><li><span><a href=\"#需求\" data-toc-modified-id=\"需求-2.9.1\"><span class=\"toc-item-num\">2.9.1&nbsp;&nbsp;</span>需求</a></span><ul class=\"toc-item\"><li><span><a href=\"#实现步骤\" data-toc-modified-id=\"实现步骤-2.9.1.1\"><span class=\"toc-item-num\">2.9.1.1&nbsp;&nbsp;</span>实现步骤</a></span></li></ul></li></ul></li><li><span><a href=\"#配置文件\" data-toc-modified-id=\"配置文件-2.10\"><span class=\"toc-item-num\">2.10&nbsp;&nbsp;</span>配置文件</a></span><ul class=\"toc-item\"><li><span><a href=\"#BASE_DIR\" data-toc-modified-id=\"BASE_DIR-2.10.1\"><span class=\"toc-item-num\">2.10.1&nbsp;&nbsp;</span>BASE_DIR</a></span></li><li><span><a href=\"#DEBUG\" data-toc-modified-id=\"DEBUG-2.10.2\"><span class=\"toc-item-num\">2.10.2&nbsp;&nbsp;</span>DEBUG</a></span></li><li><span><a href=\"#本地语言与时区\" data-toc-modified-id=\"本地语言与时区-2.10.3\"><span class=\"toc-item-num\">2.10.3&nbsp;&nbsp;</span>本地语言与时区</a></span></li></ul></li><li><span><a href=\"#静态文件\" data-toc-modified-id=\"静态文件-2.11\"><span class=\"toc-item-num\">2.11&nbsp;&nbsp;</span>静态文件</a></span><ul class=\"toc-item\"><li><span><a href=\"#示例\" data-toc-modified-id=\"示例-2.11.1\"><span class=\"toc-item-num\">2.11.1&nbsp;&nbsp;</span>示例</a></span></li></ul></li><li><span><a href=\"#App应用配置\" data-toc-modified-id=\"App应用配置-2.12\"><span class=\"toc-item-num\">2.12&nbsp;&nbsp;</span>App应用配置</a></span></li><li><span><a href=\"#模型\" data-toc-modified-id=\"模型-2.13\"><span class=\"toc-item-num\">2.13&nbsp;&nbsp;</span>模型</a></span><ul class=\"toc-item\"><li><span><a href=\"#重点\" data-toc-modified-id=\"重点-2.13.1\"><span class=\"toc-item-num\">2.13.1&nbsp;&nbsp;</span>重点</a></span></li></ul></li></ul></li><li><span><a href=\"#项目准备\" data-toc-modified-id=\"项目准备-3\"><span class=\"toc-item-num\">3&nbsp;&nbsp;</span>项目准备</a></span><ul class=\"toc-item\"><li><span><a href=\"#创建项目\" data-toc-modified-id=\"创建项目-3.1\"><span class=\"toc-item-num\">3.1&nbsp;&nbsp;</span>创建项目</a></span></li><li><span><a href=\"#创建应用\" data-toc-modified-id=\"创建应用-3.2\"><span class=\"toc-item-num\">3.2&nbsp;&nbsp;</span>创建应用</a></span></li><li><span><a href=\"#更换python解释器：按需选择\" data-toc-modified-id=\"更换python解释器：按需选择-3.3\"><span class=\"toc-item-num\">3.3&nbsp;&nbsp;</span>更换python解释器：按需选择</a></span></li><li><span><a href=\"#安装应用\" data-toc-modified-id=\"安装应用-3.4\"><span class=\"toc-item-num\">3.4&nbsp;&nbsp;</span>安装应用</a></span></li><li><span><a href=\"#本地化\" data-toc-modified-id=\"本地化-3.5\"><span class=\"toc-item-num\">3.5&nbsp;&nbsp;</span>本地化</a></span></li><li><span><a href=\"#模板路径\" data-toc-modified-id=\"模板路径-3.6\"><span class=\"toc-item-num\">3.6&nbsp;&nbsp;</span>模板路径</a></span></li><li><span><a href=\"#项目中匹配urls\" data-toc-modified-id=\"项目中匹配urls-3.7\"><span class=\"toc-item-num\">3.7&nbsp;&nbsp;</span>项目中匹配urls</a></span></li><li><span><a href=\"#应用中匹配urls.py\" data-toc-modified-id=\"应用中匹配urls.py-3.8\"><span class=\"toc-item-num\">3.8&nbsp;&nbsp;</span>应用中匹配<code>urls.py</code></a></span></li><li><span><a href=\"#准备视图\" data-toc-modified-id=\"准备视图-3.9\"><span class=\"toc-item-num\">3.9&nbsp;&nbsp;</span>准备视图</a></span></li><li><span><a href=\"#开启服务器,-测试项目\" data-toc-modified-id=\"开启服务器,-测试项目-3.10\"><span class=\"toc-item-num\">3.10&nbsp;&nbsp;</span>开启服务器, 测试项目</a></span></li></ul></li><li><span><a href=\"#配置\" data-toc-modified-id=\"配置-4\"><span class=\"toc-item-num\">4&nbsp;&nbsp;</span>配置</a></span><ul class=\"toc-item\"><li><span><a href=\"#使用MySQL数据库首先需要安装驱动程序\" data-toc-modified-id=\"使用MySQL数据库首先需要安装驱动程序-4.1\"><span class=\"toc-item-num\">4.1&nbsp;&nbsp;</span>使用<strong>MySQL</strong>数据库首先需要安装驱动程序</a></span></li><li><span><a href=\"#在Django的工程同名子目录的init.py文件中添加如下语句\" data-toc-modified-id=\"在Django的工程同名子目录的init.py文件中添加如下语句-4.2\"><span class=\"toc-item-num\">4.2&nbsp;&nbsp;</span>在Django的工程同名子目录的<strong>init</strong>.py文件中添加如下语句</a></span></li><li><span><a href=\"#修改DATABASES配置信息\" data-toc-modified-id=\"修改DATABASES配置信息-4.3\"><span class=\"toc-item-num\">4.3&nbsp;&nbsp;</span>修改<strong>DATABASES</strong>配置信息</a></span></li><li><span><a href=\"#在MySQL中创建数据库\" data-toc-modified-id=\"在MySQL中创建数据库-4.4\"><span class=\"toc-item-num\">4.4&nbsp;&nbsp;</span>在MySQL中创建数据库</a></span></li></ul></li><li><span><a href=\"#定义模型类\" data-toc-modified-id=\"定义模型类-5\"><span class=\"toc-item-num\">5&nbsp;&nbsp;</span>定义模型类</a></span><ul class=\"toc-item\"><li><span><a href=\"#定义\" data-toc-modified-id=\"定义-5.1\"><span class=\"toc-item-num\">5.1&nbsp;&nbsp;</span>定义</a></span><ul class=\"toc-item\"><li><span><a href=\"#数据库表名\" data-toc-modified-id=\"数据库表名-5.1.1\"><span class=\"toc-item-num\">5.1.1&nbsp;&nbsp;</span>数据库表名</a></span></li><li><span><a href=\"#关于主键\" data-toc-modified-id=\"关于主键-5.1.2\"><span class=\"toc-item-num\">5.1.2&nbsp;&nbsp;</span>关于主键</a></span></li><li><span><a href=\"#属性命名限制\" data-toc-modified-id=\"属性命名限制-5.1.3\"><span class=\"toc-item-num\">5.1.3&nbsp;&nbsp;</span>属性命名限制</a></span></li><li><span><a href=\"#字段类型\" data-toc-modified-id=\"字段类型-5.1.4\"><span class=\"toc-item-num\">5.1.4&nbsp;&nbsp;</span>字段类型</a></span></li><li><span><a href=\"#选项\" data-toc-modified-id=\"选项-5.1.5\"><span class=\"toc-item-num\">5.1.5&nbsp;&nbsp;</span>选项</a></span></li><li><span><a href=\"#外键\" data-toc-modified-id=\"外键-5.1.6\"><span class=\"toc-item-num\">5.1.6&nbsp;&nbsp;</span>外键</a></span></li></ul></li><li><span><a href=\"#迁移\" data-toc-modified-id=\"迁移-5.2\"><span class=\"toc-item-num\">5.2&nbsp;&nbsp;</span>迁移</a></span><ul class=\"toc-item\"><li><span><a href=\"#生成迁移文件\" data-toc-modified-id=\"生成迁移文件-5.2.1\"><span class=\"toc-item-num\">5.2.1&nbsp;&nbsp;</span>生成迁移文件</a></span></li><li><span><a href=\"#同步到数据库中\" data-toc-modified-id=\"同步到数据库中-5.2.2\"><span class=\"toc-item-num\">5.2.2&nbsp;&nbsp;</span>同步到数据库中</a></span></li></ul></li><li><span><a href=\"#添加测试数据\" data-toc-modified-id=\"添加测试数据-5.3\"><span class=\"toc-item-num\">5.3&nbsp;&nbsp;</span>添加测试数据</a></span></li><li><span><a href=\"#shell工具和查看MySQL数据库日志\" data-toc-modified-id=\"shell工具和查看MySQL数据库日志-5.4\"><span class=\"toc-item-num\">5.4&nbsp;&nbsp;</span>shell工具和查看MySQL数据库日志</a></span><ul class=\"toc-item\"><li><span><a href=\"#shell工具\" data-toc-modified-id=\"shell工具-5.4.1\"><span class=\"toc-item-num\">5.4.1&nbsp;&nbsp;</span>shell工具</a></span></li><li><span><a href=\"#查看MySQL数据库日志\" data-toc-modified-id=\"查看MySQL数据库日志-5.4.2\"><span class=\"toc-item-num\">5.4.2&nbsp;&nbsp;</span>查看MySQL数据库日志</a></span></li></ul></li></ul></li><li><span><a href=\"#数据库操作\" data-toc-modified-id=\"数据库操作-6\"><span class=\"toc-item-num\">6&nbsp;&nbsp;</span>数据库操作</a></span><ul class=\"toc-item\"><li><span><a href=\"#增加\" data-toc-modified-id=\"增加-6.1\"><span class=\"toc-item-num\">6.1&nbsp;&nbsp;</span>增加</a></span></li><li><span><a href=\"#修改\" data-toc-modified-id=\"修改-6.2\"><span class=\"toc-item-num\">6.2&nbsp;&nbsp;</span>修改</a></span></li><li><span><a href=\"#删除\" data-toc-modified-id=\"删除-6.3\"><span class=\"toc-item-num\">6.3&nbsp;&nbsp;</span>删除</a></span></li><li><span><a href=\"#基础条件查询\" data-toc-modified-id=\"基础条件查询-6.4\"><span class=\"toc-item-num\">6.4&nbsp;&nbsp;</span>基础条件查询</a></span><ul class=\"toc-item\"><li><span><a href=\"#基本查询\" data-toc-modified-id=\"基本查询-6.4.1\"><span class=\"toc-item-num\">6.4.1&nbsp;&nbsp;</span>基本查询</a></span></li><li><span><a href=\"#过滤查询\" data-toc-modified-id=\"过滤查询-6.4.2\"><span class=\"toc-item-num\">6.4.2&nbsp;&nbsp;</span>过滤查询</a></span><ul class=\"toc-item\"><li><span><a href=\"#相等\" data-toc-modified-id=\"相等-6.4.2.1\"><span class=\"toc-item-num\">6.4.2.1&nbsp;&nbsp;</span>相等</a></span></li><li><span><a href=\"#模糊查询\" data-toc-modified-id=\"模糊查询-6.4.2.2\"><span class=\"toc-item-num\">6.4.2.2&nbsp;&nbsp;</span>模糊查询</a></span></li><li><span><a href=\"#空查询\" data-toc-modified-id=\"空查询-6.4.2.3\"><span class=\"toc-item-num\">6.4.2.3&nbsp;&nbsp;</span>空查询</a></span></li><li><span><a href=\"#范围查询\" data-toc-modified-id=\"范围查询-6.4.2.4\"><span class=\"toc-item-num\">6.4.2.4&nbsp;&nbsp;</span>范围查询</a></span></li><li><span><a href=\"#比较查询\" data-toc-modified-id=\"比较查询-6.4.2.5\"><span class=\"toc-item-num\">6.4.2.5&nbsp;&nbsp;</span>比较查询</a></span></li><li><span><a href=\"#日期查询\" data-toc-modified-id=\"日期查询-6.4.2.6\"><span class=\"toc-item-num\">6.4.2.6&nbsp;&nbsp;</span>日期查询</a></span></li></ul></li></ul></li><li><span><a href=\"#F和Q对象\" data-toc-modified-id=\"F和Q对象-6.5\"><span class=\"toc-item-num\">6.5&nbsp;&nbsp;</span>F和Q对象</a></span><ul class=\"toc-item\"><li><span><a href=\"#F对象\" data-toc-modified-id=\"F对象-6.5.1\"><span class=\"toc-item-num\">6.5.1&nbsp;&nbsp;</span>F对象</a></span></li><li><span><a href=\"#Q对象\" data-toc-modified-id=\"Q对象-6.5.2\"><span class=\"toc-item-num\">6.5.2&nbsp;&nbsp;</span>Q对象</a></span></li></ul></li><li><span><a href=\"#聚合函数和排序函数\" data-toc-modified-id=\"聚合函数和排序函数-6.6\"><span class=\"toc-item-num\">6.6&nbsp;&nbsp;</span>聚合函数和排序函数</a></span><ul class=\"toc-item\"><li><span><a href=\"#聚合函数\" data-toc-modified-id=\"聚合函数-6.6.1\"><span class=\"toc-item-num\">6.6.1&nbsp;&nbsp;</span>聚合函数</a></span></li><li><span><a href=\"#排序\" data-toc-modified-id=\"排序-6.6.2\"><span class=\"toc-item-num\">6.6.2&nbsp;&nbsp;</span>排序</a></span></li></ul></li><li><span><a href=\"#关联查询\" data-toc-modified-id=\"关联查询-6.7\"><span class=\"toc-item-num\">6.7&nbsp;&nbsp;</span>关联查询</a></span><ul class=\"toc-item\"><li><span><a href=\"#关联过滤查询\" data-toc-modified-id=\"关联过滤查询-6.7.1\"><span class=\"toc-item-num\">6.7.1&nbsp;&nbsp;</span>关联过滤查询</a></span></li></ul></li><li><span><a href=\"#查询集QuerySet\" data-toc-modified-id=\"查询集QuerySet-6.8\"><span class=\"toc-item-num\">6.8&nbsp;&nbsp;</span>查询集QuerySet</a></span><ul class=\"toc-item\"><li><span><a href=\"#概念\" data-toc-modified-id=\"概念-6.8.1\"><span class=\"toc-item-num\">6.8.1&nbsp;&nbsp;</span>概念</a></span></li><li><span><a href=\"#两大特性\" data-toc-modified-id=\"两大特性-6.8.2\"><span class=\"toc-item-num\">6.8.2&nbsp;&nbsp;</span>两大特性</a></span><ul class=\"toc-item\"><li><span><a href=\"#惰性执行\" data-toc-modified-id=\"惰性执行-6.8.2.1\"><span class=\"toc-item-num\">6.8.2.1&nbsp;&nbsp;</span>惰性执行</a></span></li><li><span><a href=\"#缓存\" data-toc-modified-id=\"缓存-6.8.2.2\"><span class=\"toc-item-num\">6.8.2.2&nbsp;&nbsp;</span>缓存</a></span></li></ul></li><li><span><a href=\"#限制查询集\" data-toc-modified-id=\"限制查询集-6.8.3\"><span class=\"toc-item-num\">6.8.3&nbsp;&nbsp;</span>限制查询集</a></span></li></ul></li></ul></li><li><span><a href=\"#管理器Manager\" data-toc-modified-id=\"管理器Manager-7\"><span class=\"toc-item-num\">7&nbsp;&nbsp;</span>管理器Manager</a></span><ul class=\"toc-item\"><li><span><a href=\"#自定义管理器\" data-toc-modified-id=\"自定义管理器-7.1\"><span class=\"toc-item-num\">7.1&nbsp;&nbsp;</span>自定义管理器</a></span><ul class=\"toc-item\"><li><span><a href=\"#修改原始查询集，重写all()方法。\" data-toc-modified-id=\"修改原始查询集，重写all()方法。-7.1.1\"><span class=\"toc-item-num\">7.1.1&nbsp;&nbsp;</span>修改原始查询集，重写all()方法。</a></span><ul class=\"toc-item\"><li><span><a href=\"#打开book/models.py文件，定义类BookInfoManager\" data-toc-modified-id=\"打开book/models.py文件，定义类BookInfoManager-7.1.1.1\"><span class=\"toc-item-num\">7.1.1.1&nbsp;&nbsp;</span>打开book/models.py文件，定义类BookInfoManager</a></span></li><li><span><a href=\"#在模型类BookInfo中定义管理器\" data-toc-modified-id=\"在模型类BookInfo中定义管理器-7.1.1.2\"><span class=\"toc-item-num\">7.1.1.2&nbsp;&nbsp;</span>在模型类BookInfo中定义管理器</a></span></li><li><span><a href=\"#使用方法\" data-toc-modified-id=\"使用方法-7.1.1.3\"><span class=\"toc-item-num\">7.1.1.3&nbsp;&nbsp;</span>使用方法</a></span></li><li><span><a href=\"#视图方法\" data-toc-modified-id=\"视图方法-7.1.1.4\"><span class=\"toc-item-num\">7.1.1.4&nbsp;&nbsp;</span>视图方法</a></span></li></ul></li><li><span><a href=\"#在管理器类中补充定义新的方法\" data-toc-modified-id=\"在管理器类中补充定义新的方法-7.1.2\"><span class=\"toc-item-num\">7.1.2&nbsp;&nbsp;</span>在管理器类中补充定义新的方法</a></span><ul class=\"toc-item\"><li><span><a href=\"#打开book/models.py文件，定义方法create。\" data-toc-modified-id=\"打开book/models.py文件，定义方法create。-7.1.2.1\"><span class=\"toc-item-num\">7.1.2.1&nbsp;&nbsp;</span>打开book/models.py文件，定义方法create。</a></span></li><li><span><a href=\"#为模型类BookInfo定义管理器books语法如下\" data-toc-modified-id=\"为模型类BookInfo定义管理器books语法如下-7.1.2.2\"><span class=\"toc-item-num\">7.1.2.2&nbsp;&nbsp;</span>为模型类BookInfo定义管理器books语法如下</a></span></li><li><span><a href=\"#调用语法如下：\" data-toc-modified-id=\"调用语法如下：-7.1.2.3\"><span class=\"toc-item-num\">7.1.2.3&nbsp;&nbsp;</span>调用语法如下：</a></span></li></ul></li></ul></li></ul></li><li><span><a href=\"#视图\" data-toc-modified-id=\"视图-8\"><span class=\"toc-item-num\">8&nbsp;&nbsp;</span>视图</a></span><ul class=\"toc-item\"><li><span><a href=\"#视图介绍和项目准备\" data-toc-modified-id=\"视图介绍和项目准备-8.1\"><span class=\"toc-item-num\">8.1&nbsp;&nbsp;</span>视图介绍和项目准备</a></span><ul class=\"toc-item\"><li><span><a href=\"#视图介绍\" data-toc-modified-id=\"视图介绍-8.1.1\"><span class=\"toc-item-num\">8.1.1&nbsp;&nbsp;</span>视图介绍</a></span></li><li><span><a href=\"#项目准备\" data-toc-modified-id=\"项目准备-8.1.2\"><span class=\"toc-item-num\">8.1.2&nbsp;&nbsp;</span>项目准备</a></span><ul class=\"toc-item\"><li><span><a href=\"#mysql数据库使用之前的book\" data-toc-modified-id=\"mysql数据库使用之前的book-8.1.2.1\"><span class=\"toc-item-num\">8.1.2.1&nbsp;&nbsp;</span>mysql数据库使用之前的book</a></span></li><li><span><a href=\"#URLconf\" data-toc-modified-id=\"URLconf-8.1.2.2\"><span class=\"toc-item-num\">8.1.2.2&nbsp;&nbsp;</span>URLconf</a></span></li><li><span><a href=\"#应用中urls.py：\" data-toc-modified-id=\"应用中urls.py：-8.1.2.3\"><span class=\"toc-item-num\">8.1.2.3&nbsp;&nbsp;</span>应用中<code>urls.py</code>：</a></span></li><li><span><a href=\"#视图：测试项目逻辑\" data-toc-modified-id=\"视图：测试项目逻辑-8.1.2.4\"><span class=\"toc-item-num\">8.1.2.4&nbsp;&nbsp;</span>视图：测试项目逻辑</a></span></li><li><span><a href=\"#在models.py-文件中定义模型类\" data-toc-modified-id=\"在models.py-文件中定义模型类-8.1.2.5\"><span class=\"toc-item-num\">8.1.2.5&nbsp;&nbsp;</span>在models.py 文件中定义模型类</a></span></li></ul></li></ul></li><li><span><a href=\"#URLconf\" data-toc-modified-id=\"URLconf-8.2\"><span class=\"toc-item-num\">8.2&nbsp;&nbsp;</span>URLconf</a></span><ul class=\"toc-item\"><li><span><a href=\"#配置URLconf\" data-toc-modified-id=\"配置URLconf-8.2.1\"><span class=\"toc-item-num\">8.2.1&nbsp;&nbsp;</span>配置URLconf</a></span><ul class=\"toc-item\"><li><span><a href=\"#settings.py中\" data-toc-modified-id=\"settings.py中-8.2.1.1\"><span class=\"toc-item-num\">8.2.1.1&nbsp;&nbsp;</span><code>settings.py</code>中</a></span></li><li><span><a href=\"#项目中urls.py\" data-toc-modified-id=\"项目中urls.py-8.2.1.2\"><span class=\"toc-item-num\">8.2.1.2&nbsp;&nbsp;</span>项目中<code>urls.py</code></a></span></li><li><span><a href=\"#应用中urls.py\" data-toc-modified-id=\"应用中urls.py-8.2.1.3\"><span class=\"toc-item-num\">8.2.1.3&nbsp;&nbsp;</span>应用中<code>urls.py</code></a></span></li><li><span><a href=\"#提示\" data-toc-modified-id=\"提示-8.2.1.4\"><span class=\"toc-item-num\">8.2.1.4&nbsp;&nbsp;</span>提示</a></span></li></ul></li></ul></li><li><span><a href=\"#路由命名与reverse反解析（逆向）\" data-toc-modified-id=\"路由命名与reverse反解析（逆向）-8.3\"><span class=\"toc-item-num\">8.3&nbsp;&nbsp;</span>路由命名与reverse反解析（逆向）</a></span><ul class=\"toc-item\"><li><span><a href=\"#路由命名\" data-toc-modified-id=\"路由命名-8.3.1\"><span class=\"toc-item-num\">8.3.1&nbsp;&nbsp;</span>路由命名</a></span></li><li><span><a href=\"#reverse反解析\" data-toc-modified-id=\"reverse反解析-8.3.2\"><span class=\"toc-item-num\">8.3.2&nbsp;&nbsp;</span>reverse反解析</a></span></li></ul></li><li><span><a href=\"#HttpRequest对象\" data-toc-modified-id=\"HttpRequest对象-8.4\"><span class=\"toc-item-num\">8.4&nbsp;&nbsp;</span>HttpRequest对象</a></span><ul class=\"toc-item\"><li><span><a href=\"#URL路径参数\" data-toc-modified-id=\"URL路径参数-8.4.1\"><span class=\"toc-item-num\">8.4.1&nbsp;&nbsp;</span>URL路径参数</a></span><ul class=\"toc-item\"><li><span><a href=\"#位置参数\" data-toc-modified-id=\"位置参数-8.4.1.1\"><span class=\"toc-item-num\">8.4.1.1&nbsp;&nbsp;</span>位置参数</a></span></li><li><span><a href=\"#关键字参数\" data-toc-modified-id=\"关键字参数-8.4.1.2\"><span class=\"toc-item-num\">8.4.1.2&nbsp;&nbsp;</span>关键字参数</a></span></li></ul></li><li><span><a href=\"#Django中的QueryDict对象\" data-toc-modified-id=\"Django中的QueryDict对象-8.4.2\"><span class=\"toc-item-num\">8.4.2&nbsp;&nbsp;</span>Django中的QueryDict对象</a></span></li><li><span><a href=\"#查询字符串Query-String\" data-toc-modified-id=\"查询字符串Query-String-8.4.3\"><span class=\"toc-item-num\">8.4.3&nbsp;&nbsp;</span>查询字符串Query String</a></span></li><li><span><a href=\"#请求体\" data-toc-modified-id=\"请求体-8.4.4\"><span class=\"toc-item-num\">8.4.4&nbsp;&nbsp;</span>请求体</a></span><ul class=\"toc-item\"><li><span><a href=\"#表单类型-Form-Data\" data-toc-modified-id=\"表单类型-Form-Data-8.4.4.1\"><span class=\"toc-item-num\">8.4.4.1&nbsp;&nbsp;</span>表单类型 Form Data</a></span></li><li><span><a href=\"#非表单类型-Non-Form-Data\" data-toc-modified-id=\"非表单类型-Non-Form-Data-8.4.4.2\"><span class=\"toc-item-num\">8.4.4.2&nbsp;&nbsp;</span>非表单类型 Non-Form Data</a></span></li></ul></li><li><span><a href=\"#请求头\" data-toc-modified-id=\"请求头-8.4.5\"><span class=\"toc-item-num\">8.4.5&nbsp;&nbsp;</span>请求头</a></span></li><li><span><a href=\"#其他常用HttpRequest对象属性\" data-toc-modified-id=\"其他常用HttpRequest对象属性-8.4.6\"><span class=\"toc-item-num\">8.4.6&nbsp;&nbsp;</span>其他常用HttpRequest对象属性</a></span></li></ul></li><li><span><a href=\"#HttpResponse对象\" data-toc-modified-id=\"HttpResponse对象-8.5\"><span class=\"toc-item-num\">8.5&nbsp;&nbsp;</span>HttpResponse对象</a></span><ul class=\"toc-item\"><li><span><a href=\"#HttpResponse\" data-toc-modified-id=\"HttpResponse-8.5.1\"><span class=\"toc-item-num\">8.5.1&nbsp;&nbsp;</span>HttpResponse</a></span></li><li><span><a href=\"#HttpResponse子类\" data-toc-modified-id=\"HttpResponse子类-8.5.2\"><span class=\"toc-item-num\">8.5.2&nbsp;&nbsp;</span>HttpResponse子类</a></span></li><li><span><a href=\"#JsonResponse\" data-toc-modified-id=\"JsonResponse-8.5.3\"><span class=\"toc-item-num\">8.5.3&nbsp;&nbsp;</span>JsonResponse</a></span></li><li><span><a href=\"#redirect重定向\" data-toc-modified-id=\"redirect重定向-8.5.4\"><span class=\"toc-item-num\">8.5.4&nbsp;&nbsp;</span>redirect重定向</a></span></li></ul></li><li><span><a href=\"#状态保持\" data-toc-modified-id=\"状态保持-8.6\"><span class=\"toc-item-num\">8.6&nbsp;&nbsp;</span>状态保持</a></span><ul class=\"toc-item\"><li><span><a href=\"#Cookie\" data-toc-modified-id=\"Cookie-8.6.1\"><span class=\"toc-item-num\">8.6.1&nbsp;&nbsp;</span>Cookie</a></span><ul class=\"toc-item\"><li><span><a href=\"#Cookie的特点\" data-toc-modified-id=\"Cookie的特点-8.6.1.1\"><span class=\"toc-item-num\">8.6.1.1&nbsp;&nbsp;</span>Cookie的特点</a></span></li></ul></li></ul></li><li><span><a href=\"#Session\" data-toc-modified-id=\"Session-8.7\"><span class=\"toc-item-num\">8.7&nbsp;&nbsp;</span>Session</a></span><ul class=\"toc-item\"><li><span><a href=\"#启用Session\" data-toc-modified-id=\"启用Session-8.7.1\"><span class=\"toc-item-num\">8.7.1&nbsp;&nbsp;</span>启用Session</a></span></li><li><span><a href=\"#存储方式\" data-toc-modified-id=\"存储方式-8.7.2\"><span class=\"toc-item-num\">8.7.2&nbsp;&nbsp;</span>存储方式</a></span><ul class=\"toc-item\"><li><span><a href=\"#数据库\" data-toc-modified-id=\"数据库-8.7.2.1\"><span class=\"toc-item-num\">8.7.2.1&nbsp;&nbsp;</span>数据库</a></span></li><li><span><a href=\"#本地缓存\" data-toc-modified-id=\"本地缓存-8.7.2.2\"><span class=\"toc-item-num\">8.7.2.2&nbsp;&nbsp;</span>本地缓存</a></span></li><li><span><a href=\"#混合存储\" data-toc-modified-id=\"混合存储-8.7.2.3\"><span class=\"toc-item-num\">8.7.2.3&nbsp;&nbsp;</span>混合存储</a></span></li><li><span><a href=\"#Redis\" data-toc-modified-id=\"Redis-8.7.2.4\"><span class=\"toc-item-num\">8.7.2.4&nbsp;&nbsp;</span>Redis</a></span></li></ul></li><li><span><a href=\"#Session操作\" data-toc-modified-id=\"Session操作-8.7.3\"><span class=\"toc-item-num\">8.7.3&nbsp;&nbsp;</span>Session操作</a></span></li></ul></li><li><span><a href=\"#类视图\" data-toc-modified-id=\"类视图-8.8\"><span class=\"toc-item-num\">8.8&nbsp;&nbsp;</span>类视图</a></span><ul class=\"toc-item\"><li><span><a href=\"#注册视图处理get和post请求\" data-toc-modified-id=\"注册视图处理get和post请求-8.8.1\"><span class=\"toc-item-num\">8.8.1&nbsp;&nbsp;</span>注册视图处理get和post请求</a></span></li><li><span><a href=\"#类视图使用\" data-toc-modified-id=\"类视图使用-8.8.2\"><span class=\"toc-item-num\">8.8.2&nbsp;&nbsp;</span>类视图使用</a></span></li><li><span><a href=\"#类视图原理\" data-toc-modified-id=\"类视图原理-8.8.3\"><span class=\"toc-item-num\">8.8.3&nbsp;&nbsp;</span>类视图原理</a></span></li><li><span><a href=\"#类视图使用装饰器\" data-toc-modified-id=\"类视图使用装饰器-8.8.4\"><span class=\"toc-item-num\">8.8.4&nbsp;&nbsp;</span>类视图使用装饰器</a></span><ul class=\"toc-item\"><li><span><a href=\"#在URL配置中装饰\" data-toc-modified-id=\"在URL配置中装饰-8.8.4.1\"><span class=\"toc-item-num\">8.8.4.1&nbsp;&nbsp;</span>在URL配置中装饰</a></span></li><li><span><a href=\"#在类视图中装饰\" data-toc-modified-id=\"在类视图中装饰-8.8.4.2\"><span class=\"toc-item-num\">8.8.4.2&nbsp;&nbsp;</span>在类视图中装饰</a></span></li><li><span><a href=\"#构造Mixin扩展类\" data-toc-modified-id=\"构造Mixin扩展类-8.8.4.3\"><span class=\"toc-item-num\">8.8.4.3&nbsp;&nbsp;</span>构造Mixin扩展类</a></span></li></ul></li></ul></li></ul></li><li><span><a href=\"#中间件\" data-toc-modified-id=\"中间件-9\"><span class=\"toc-item-num\">9&nbsp;&nbsp;</span>中间件</a></span><ul class=\"toc-item\"><li><span><a href=\"#中间件的定义方法\" data-toc-modified-id=\"中间件的定义方法-9.1\"><span class=\"toc-item-num\">9.1&nbsp;&nbsp;</span>中间件的定义方法</a></span></li><li><span><a href=\"#多个中间件的执行顺序\" data-toc-modified-id=\"多个中间件的执行顺序-9.2\"><span class=\"toc-item-num\">9.2&nbsp;&nbsp;</span>多个中间件的执行顺序</a></span></li></ul></li><li><span><a href=\"#模版\" data-toc-modified-id=\"模版-10\"><span class=\"toc-item-num\">10&nbsp;&nbsp;</span>模版</a></span><ul class=\"toc-item\"><li><span><a href=\"#配置\" data-toc-modified-id=\"配置-10.1\"><span class=\"toc-item-num\">10.1&nbsp;&nbsp;</span>配置</a></span></li><li><span><a href=\"#定义模板\" data-toc-modified-id=\"定义模板-10.2\"><span class=\"toc-item-num\">10.2&nbsp;&nbsp;</span>定义模板</a></span></li><li><span><a href=\"#模板渲染\" data-toc-modified-id=\"模板渲染-10.3\"><span class=\"toc-item-num\">10.3&nbsp;&nbsp;</span>模板渲染</a></span></li><li><span><a href=\"#模板语法\" data-toc-modified-id=\"模板语法-10.4\"><span class=\"toc-item-num\">10.4&nbsp;&nbsp;</span>模板语法</a></span><ul class=\"toc-item\"><li><span><a href=\"#模板变量\" data-toc-modified-id=\"模板变量-10.4.1\"><span class=\"toc-item-num\">10.4.1&nbsp;&nbsp;</span>模板变量</a></span></li><li><span><a href=\"#模板语句\" data-toc-modified-id=\"模板语句-10.4.2\"><span class=\"toc-item-num\">10.4.2&nbsp;&nbsp;</span>模板语句</a></span></li><li><span><a href=\"#过滤器\" data-toc-modified-id=\"过滤器-10.4.3\"><span class=\"toc-item-num\">10.4.3&nbsp;&nbsp;</span>过滤器</a></span></li><li><span><a href=\"#注释\" data-toc-modified-id=\"注释-10.4.4\"><span class=\"toc-item-num\">10.4.4&nbsp;&nbsp;</span>注释</a></span></li><li><span><a href=\"#模板继承\" data-toc-modified-id=\"模板继承-10.4.5\"><span class=\"toc-item-num\">10.4.5&nbsp;&nbsp;</span>模板继承</a></span></li></ul></li></ul></li><li><span><a href=\"#Admin站点\" data-toc-modified-id=\"Admin站点-11\"><span class=\"toc-item-num\">11&nbsp;&nbsp;</span>Admin站点</a></span><ul class=\"toc-item\"><li><span><a href=\"#管理界面本地化\" data-toc-modified-id=\"管理界面本地化-11.1\"><span class=\"toc-item-num\">11.1&nbsp;&nbsp;</span>管理界面本地化</a></span></li><li><span><a href=\"#创建超级管理员\" data-toc-modified-id=\"创建超级管理员-11.2\"><span class=\"toc-item-num\">11.2&nbsp;&nbsp;</span>创建超级管理员</a></span></li><li><span><a href=\"#注册模型类\" data-toc-modified-id=\"注册模型类-11.3\"><span class=\"toc-item-num\">11.3&nbsp;&nbsp;</span>注册模型类</a></span></li><li><span><a href=\"#定义与使用Admin管理类\" data-toc-modified-id=\"定义与使用Admin管理类-11.4\"><span class=\"toc-item-num\">11.4&nbsp;&nbsp;</span>定义与使用Admin管理类</a></span></li><li><span><a href=\"#调整列表页展示\" data-toc-modified-id=\"调整列表页展示-11.5\"><span class=\"toc-item-num\">11.5&nbsp;&nbsp;</span>调整列表页展示</a></span><ul class=\"toc-item\"><li><span><a href=\"#页大小\" data-toc-modified-id=\"页大小-11.5.1\"><span class=\"toc-item-num\">11.5.1&nbsp;&nbsp;</span>页大小</a></span></li><li><span><a href=\"#&quot;操作选项&quot;的位置\" data-toc-modified-id=\"&quot;操作选项&quot;的位置-11.5.2\"><span class=\"toc-item-num\">11.5.2&nbsp;&nbsp;</span>\"操作选项\"的位置</a></span></li><li><span><a href=\"#列表中的列\" data-toc-modified-id=\"列表中的列-11.5.3\"><span class=\"toc-item-num\">11.5.3&nbsp;&nbsp;</span>列表中的列</a></span></li><li><span><a href=\"#将方法作为列\" data-toc-modified-id=\"将方法作为列-11.5.4\"><span class=\"toc-item-num\">11.5.4&nbsp;&nbsp;</span>将方法作为列</a></span></li><li><span><a href=\"#关联对象\" data-toc-modified-id=\"关联对象-11.5.5\"><span class=\"toc-item-num\">11.5.5&nbsp;&nbsp;</span>关联对象</a></span></li><li><span><a href=\"#右侧栏过滤器\" data-toc-modified-id=\"右侧栏过滤器-11.5.6\"><span class=\"toc-item-num\">11.5.6&nbsp;&nbsp;</span>右侧栏过滤器</a></span></li><li><span><a href=\"#搜索框\" data-toc-modified-id=\"搜索框-11.5.7\"><span class=\"toc-item-num\">11.5.7&nbsp;&nbsp;</span>搜索框</a></span></li></ul></li><li><span><a href=\"#调整编辑页展示\" data-toc-modified-id=\"调整编辑页展示-11.6\"><span class=\"toc-item-num\">11.6&nbsp;&nbsp;</span>调整编辑页展示</a></span><ul class=\"toc-item\"><li><span><a href=\"#显示字段\" data-toc-modified-id=\"显示字段-11.6.1\"><span class=\"toc-item-num\">11.6.1&nbsp;&nbsp;</span>显示字段</a></span></li><li><span><a href=\"#分组显示\" data-toc-modified-id=\"分组显示-11.6.2\"><span class=\"toc-item-num\">11.6.2&nbsp;&nbsp;</span>分组显示</a></span></li><li><span><a href=\"#关联对象\" data-toc-modified-id=\"关联对象-11.6.3\"><span class=\"toc-item-num\">11.6.3&nbsp;&nbsp;</span>关联对象</a></span></li></ul></li><li><span><a href=\"#调整站点信息\" data-toc-modified-id=\"调整站点信息-11.7\"><span class=\"toc-item-num\">11.7&nbsp;&nbsp;</span>调整站点信息</a></span></li></ul></li><li><span><a href=\"#上传图片\" data-toc-modified-id=\"上传图片-12\"><span class=\"toc-item-num\">12&nbsp;&nbsp;</span>上传图片</a></span><ul class=\"toc-item\"><li><span><a href=\"#配置\" data-toc-modified-id=\"配置-12.1\"><span class=\"toc-item-num\">12.1&nbsp;&nbsp;</span>配置</a></span></li><li><span><a href=\"#为模型类添加ImageField字段\" data-toc-modified-id=\"为模型类添加ImageField字段-12.2\"><span class=\"toc-item-num\">12.2&nbsp;&nbsp;</span>为模型类添加ImageField字段</a></span></li><li><span><a href=\"#使用Admin站点上传图片\" data-toc-modified-id=\"使用Admin站点上传图片-12.3\"><span class=\"toc-item-num\">12.3&nbsp;&nbsp;</span>使用Admin站点上传图片</a></span></li></ul></li></ul></div>"
   ]
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "\n",
    "\n",
    "![](./assets/web1.png)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Django介绍\n",
    "\n",
    "##  简介\n",
    "\n",
    "Django，**发音为[`dʒæŋɡəʊ]**，是用python语言写的开源web开发框架，并遵循MVC设计。劳伦斯出版集团为了开发以新闻内容为主的网站，而开发出来了这个框架，于2005年7月在BSD许可证下发布。这个名称来源于比利时的爵士音乐家DjangoReinhardt，他是一个吉普赛人，主要以演奏吉它为主，还演奏过小提琴等。\n",
    "\n",
    "Django的**主要目的是简便、快速的开发数据库驱动的网站。\n"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "##  特点\n",
    "\n",
    "###  重量级框架\n",
    "\n",
    "对比Flask框架，Django原生提供了众多的功能组件，让开发更简便快速。\n",
    "\n",
    "- 提供项目工程管理的自动化脚本工具\n",
    "- 数据库ORM支持（对象关系映射，英语：Object Relational Mapping）\n",
    "- 模板\n",
    "- 表单\n",
    "- Admin管理站点\n",
    "- 文件管理\n",
    "- 认证权限\n",
    "- session机制\n",
    "- 缓存\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "#### MVC模式说明\n",
    "\n",
    "![image.png](./assets/mvc.png)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "#### Django的MVT\n",
    "\n",
    "![image.png](./assets/mvt.png)\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 环境安装\n",
    "\n",
    " 1. 创建虚拟环境\n",
    " 2. 安装Django\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 创建第一个Django项目\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 创建工程\n",
    "\n",
    "在使用Flask框架时，项目工程目录的组织与创建是需要我们自己手动创建完成的。\n",
    "\n",
    "在django中，项目工程目录可以借助django提供的命令帮助我们创建。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "#### 创建\n",
    "\n",
    "创建工程的命令为：\n",
    "\n",
    "```\n",
    "django-admin startproject 工程名称\n",
    "```\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 工程目录说明\n",
    "\n",
    "查看创建的工程目录，结构如下\n",
    "\n",
    "\n",
    "- 与项目同名的目录，此处为bookmanager。\n",
    "- **settings.py**是项目的整体配置文件。\n",
    "- **urls.py**是项目的URL配置文件。\n",
    "- **wsgi.py**是项目与WSGI兼容的Web服务器入口。\n",
    "- **manage.py**是项目管理文件，通过它管理项目。\n",
    "- **asgi.py**ASGI服务器组件,ASGI的A就是Async，也就是异步的意思，ASGI简单的来说就是异步的WSGI"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "####  运行开发服务器\n",
    "\n",
    "在开发阶段，为了能够快速预览到开发的效果，django提供了一个纯python编写的轻量级web服务器，仅在开发阶段使用。\n",
    "\n",
    "运行服务器命令如下：\n",
    "\n",
    "```\n",
    "python manage.py runserver ip:端口\n",
    "或：\n",
    "python manage.py runserver\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 创建子应用\n",
    "\n",
    "\n",
    "####  创建\n",
    "\n",
    "在django中，创建子应用模块目录仍然可以通过命令来操作，即：\n",
    "\n",
    "```\n",
    "python manage.py startapp 子应用名称\n",
    "```\n",
    "\n",
    "**manage.py**为上述创建工程时自动生成的管理文件。\n",
    "\n",
    "\n",
    "####  子应用目录说明\n",
    "\n",
    "查看此时的工程目录，结构如下：\n",
    "\n",
    "\n",
    "- **admin.py**文件跟网站的后台管理站点配置相关。\n",
    "- **apps.py**文件用于配置当前子应用的相关信息。\n",
    "- **migrations**目录用于存放数据库迁移历史文件。\n",
    "- **models.py**文件用户保存数据库模型类。\n",
    "- **tests.py**文件用于开发测试用例，编写单元测试。\n",
    "- **views.py**文件用于编写Web应用视图。\n",
    "\n",
    "####  注册安装子应用\n",
    "\n",
    "创建出来的子应用目录文件虽然被放到了工程项目目录中，但是django工程并不能立即直接使用该子应用，需要注册安装后才能使用。\n",
    "\n",
    "在工程配置文件settings.py中，**INSTALLED_APPS**项保存了工程中已经注册安装的子应用，初始工程中的INSTALLED_APPS如下：\n",
    "\n",
    "```python\n",
    "# Application definition\n",
    "\n",
    "INSTALLED_APPS = [\n",
    "    'django.contrib.admin',\n",
    "    'django.contrib.auth',\n",
    "    'django.contrib.contenttypes',\n",
    "    'django.contrib.sessions',\n",
    "    'django.contrib.messages',\n",
    "    'django.contrib.staticfiles',\n",
    "]\n",
    "```\n",
    "**注册安装一个子应用的方法，即是将子应用的配置信息文件apps.py中的Config类添加到INSTALLED_APPS列表中。**\n"
   ]
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "## 模型\n",
    "\n",
    "\n",
    "> 书籍信息表\n",
    "\n",
    "| 字段名 | 字段类型  | 字段说明 |\n",
    "| :----- | :-------- | :------- |\n",
    "| id     | AutoField | 主键     |\n",
    "| name   | CharField | 书名     |\n",
    "\n",
    "| id   | name     |\n",
    "| :--- | :------- |\n",
    "| 1    | 西游记   |\n",
    "| 2    | 三国演义 |\n",
    "\n",
    "> 人物信息表\n",
    "\n",
    "| 字段名 | 字段类型     | 字段说明 |\n",
    "| :----- | :----------- | :------- |\n",
    "| id     | AutoField    | 主键     |\n",
    "| name   | CharField    | 人名     |\n",
    "| gender | BooleanField | 性别     |\n",
    "| book   | ForeignKey   | 外键     |\n",
    "\n",
    "| id   | name   | gender | book |\n",
    "| :--- | :----- | :----- | :--- |\n",
    "| 1    | 唐僧 | False  | 1    |\n",
    "| 2    | 白骨精 | True   | 1    |\n",
    "| 3    | 吕布   | False  | 2    |\n",
    "| 4    | 貂蝉   | True   | 2    |\n",
    "\n",
    "### 使用Django进行数据库开发的提示 ：\n",
    "\n",
    "- `MVT`设计模式中的`Model`, 专门负责和数据库交互.对应`(models.py)`\n",
    "- 由于`Model`中内嵌了`ORM框架`, 所以不需要直接面向数据库编程.\n",
    "- 而是定义模型类, 通过`模型类和对象`完成数据库表的`增删改查`.\n",
    "- `ORM框架`就是把数据库表的行与相应的对象建立关联, 互相转换.使得数据库的操作面向对象.\n",
    "\n",
    "### 使用Django进行数据库开发的步骤 ：\n",
    "\n",
    "1. 定义模型类\n",
    "2. 模型迁移\n",
    "3. 操作数据库\n",
    "\n",
    "####  定义模型类\n",
    "\n",
    "- 根据书籍表结构设计模型类:\n",
    "\n",
    "  - 模型类：BookInfo\n",
    "  - 书籍名称字段：name\n",
    "\n",
    "- 根据人物表结构设计模型类：\n",
    "\n",
    "  - 模型类：PeopleInfo\n",
    "  - 人物姓名字段：name\n",
    "  - 人物性别字段：gender\n",
    "  - 外键约束：book\n",
    "    - 外键要指定所属的模型类`book = models.ForeignKey(BookInfo)`\n",
    "\n",
    "- 说明 :\n",
    "\n",
    "  - 书籍-人物的关系为一对多. 一本书中可以有多个英雄.\n",
    "  - 不需要定义主键字段, 在生成表时会自动添加, 并且值为自增长.\n",
    "\n",
    "- 根据数据库表的设计\n",
    "\n",
    "  - 在`models.py`中定义模型类,继承自`models.Model`\n",
    "\n",
    "    ```python\n",
    "    from django.db import models\n",
    "\n",
    "    # Create your models here.\n",
    "    # 准备书籍列表信息的模型类\n",
    "    class BookInfo(models.Model):\n",
    "        # 创建字段，字段类型...\n",
    "        name = models.CharField(max_length=10)\n",
    "\n",
    "    # 准备人物列表信息的模型类\n",
    "    class PeopleInfo(models.Model):\n",
    "        name = models.CharField(max_length=10)\n",
    "        gender = models.BooleanField()\n",
    "        # 外键约束：人物属于哪本书\n",
    "        book = models.ForeignKey(BookInfo)\n",
    "    ```\n",
    "\n",
    "####  模型迁移 （建表）\n",
    "\n",
    "- 迁移由两步完成 :\n",
    "\n",
    "  - 生成迁移文件：根据模型类生成创建表的语句\n",
    "\n",
    "    ```\n",
    "    python manage.py makemigrations\n",
    "    ```\n",
    "\n",
    "  - 执行迁移：根据第一步生成的语句在数据库中创建表\n",
    "\n",
    "    ```\n",
    "    python manage.py migrate\n",
    "    ```\n",
    "\n",
    "- **提示：默认采用**`sqlite3`**数据库来存储数据**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 站点管理\n",
    "\n",
    "- **站点**: 分为`内容发布`和`公共访问`两部分\n",
    "\n",
    "- **内容发布**的部分由网站的管理员负责查看、添加、修改、删除数据\n",
    "\n",
    "- `Django`能够根据定义的模型类自动地生成管理模块\n",
    "\n",
    "- 使用Django的管理模块, 需要按照如下步骤操作 :\n",
    "\n",
    "  - 1.管理界面本地化\n",
    "  - 2.创建管理员\n",
    "  - 3.注册模型类\n",
    "  - 4.发布内容到数据库\n",
    "\n",
    "### 管理界面本地化\n",
    "\n",
    "- 本地化是将显示的语言、时间等使用本地的习惯，这里的本地化就是进行中国化.\n",
    "\n",
    "- 中国大陆地区使用`简体中文`, 时区使用`亚洲/上海时区`, 注意这里不使用北京时区.\n",
    "\n",
    "```python\n",
    "# Django是一个支持国际化和本地化的框架，因此刚才我们看到的Django项目的默认首页也是支持国际化的，\n",
    "# 我们可以通过修改配置文件将默认语言修改为中文，时区设置为东八区。\n",
    "LANGUAGE_CODE = 'zh-Hans'\n",
    "# LANGUAGE_CODE = 'en-us'  # 本地化前\n",
    "\n",
    "TIME_ZONE = 'Asia/Shanghai'\n",
    "# TIME_ZONE = 'UTC'  # 本地化前\n",
    "```\n",
    "\n",
    "### 创建管理员\n",
    "\n",
    "- 创建管理员的命令 :\n",
    "\n",
    "  ```\n",
    "    python manage.py createsuperuser\n",
    "  ```\n",
    "\n",
    "- 按提示输入用户名、邮箱、密码\n",
    "- 重置密码\n",
    "    ```\n",
    "    python manager.py changepassword 用户名\n",
    "    ```\n",
    "\n",
    "- 登陆站点 :`http://127.0.0.1:8000/admin`\n",
    "\n",
    "  - 需要服务器是启动状态\n",
    "\n",
    "\n",
    "- 登陆站点成功\n",
    "\n",
    "  - 站点界面中没有书籍和人物管理入口,因为没有注册模型类\n",
    "\n",
    "\n",
    "### 注册模型类\n",
    "\n",
    "- 在`应用`的`admin.py`文件中注册模型类\n",
    "\n",
    "  - 需要导入模型模块 :`from book.models import BookInfo,PeopleInfo`\n",
    "\n",
    "    \n",
    "- 注册模型后\n",
    "\n",
    "> **注册模型成功后, 就可以在站点管理界面方便快速的管理数据.**\n",
    "\n",
    "### 发布内容到数据库\n",
    "\n",
    "\n",
    "- 发布内容后，优化模型类展示\n",
    "\n",
    "    ```python\n",
    "    # 准备书籍列表信息的模型类\n",
    "    class BookInfo(models.Model):\n",
    "        # 创建字段，字段类型...\n",
    "        name = models.CharField(max_length=10)\n",
    "\n",
    "        def __str__(self):\n",
    "            \"\"\"将模型类以字符串的方式输出\"\"\"\n",
    "            return self.name\n",
    "    ```\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 视图和URL\n",
    "\n",
    "- 站点管理页面做好了, 接下来就要做`公共访问`的页面了.\n",
    "\n",
    "- 对于`Django`的设计框架`MVT`\n",
    "\n",
    "  - 用户在URL中请求的是视图.\n",
    "  - 视图接收请求后进行处理.\n",
    "  - 并将处理的结果返回给请求者.\n",
    "\n",
    "- 使用视图时需要进行两步操作\n",
    "\n",
    "  - 1.定义视图\n",
    "  - 2.配置URLconf"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 定义视图\n",
    "\n",
    "- 视图就是一个`Python`函数，被定义在`应用`的`views.py`中.\n",
    "\n",
    "- 视图的第一个参数是`HttpRequest`类型的对象`reqeust`，包含了所有`请求信息`.\n",
    "\n",
    "- 视图必须返回`HttpResponse对象`，包含返回给请求者的`响应信息`.\n",
    "\n",
    "- 需要导入`HttpResponse`模块 :`from django.http import HttpResponse`\n",
    "\n",
    "- 定义视图函数 : 响应字符串`OK!`给客户端   `return HttpResponse('ok')`\n",
    "\n",
    "> **思考 : 如何才能让请求找到视图?**\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 配置URLconf\n",
    "\n",
    "- 查找视图的过程 :\n",
    "\n",
    "  - 1.请求者在浏览器地址栏中输入URL, 请求到网站.\n",
    "\n",
    "  - 2.网站获取URL信息.\n",
    "\n",
    "  - 3.然后与编写好的URLconf逐条匹配.\n",
    "\n",
    "  - 4.如果匹配成功则调用对应的视图.\n",
    "\n",
    "  - 5.如果所有的URLconf都没有匹配成功.则返回404错误.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 需要两步完成`URLconf`配置\n",
    "\n",
    "  - 1.在`项目`中定义`URLconf`\n",
    "  - 2.在`应用`中定义`URLconf`\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- 提示：一条`URLconf`包括URL规则、视图两部分"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 模板\n",
    "\n",
    "> 思考 : 网站如何向客户端返回一个漂亮的页面呢？\n",
    "\n",
    "- 提示 :\n",
    "  - 漂亮的页面需要`html`、`css`、`js`.\n",
    "  - 可以把这一堆字段串全都写到视图中, 作为`HttpResponse()`的参数,响应给客户端.\n",
    "- 问题 :\n",
    "  - 视图部分代码臃肿, 耦合度高.\n",
    "  - 这样定义的字符串是不会出任何效果和错误的.\n",
    "  - 效果无法及时查看.有错也不容易及时发现.\n",
    "- 设想 :\n",
    "  - 是否可以有一个专门定义前端页面的地方, 效果可以及时展示,错误可以及时发现,并且可以降低模块间耦合度!\n",
    "- 解决问题 :**模板**\n",
    "  - `MVT`设计模式中的`T`,`Template`\n",
    "- **在**`Django`**中, 将前端的内容定义在模板中, 然后再把模板交给视图调用, 各种漂亮、炫酷的效果就出现了.**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 模板使用步骤\n",
    "\n",
    "- 1.创建模板\n",
    "- 2.设置模板查找路径\n",
    "- 3.模板接收视图传入的数据\n",
    "- 4.模板处理数据"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 展示书籍列表\n",
    "\n",
    "### 需求\n",
    "\n",
    "- 展示书籍列表\n",
    "    - python基础\n",
    "    - python高级\n",
    "    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 实现步骤\n",
    "\n",
    "1. 创建视图\n",
    "2. 创建模板\n",
    "3. 配置URLconf\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "##### 创建视图\n",
    "\n",
    "- 查询数据库数据\n",
    "\n",
    "- 构造上下文\n",
    "\n",
    "- 传递上下文到模板\n",
    "\n",
    "    ```python\n",
    "    # 定义视图：提供书籍列表信息\n",
    "    def bookList(request):\n",
    "      # 查询数据库书籍列表数据\n",
    "      books = BookInfo.objects.all()\n",
    "      # 构造上下文\n",
    "      context = {'books':books}\n",
    "      # 数据交给模板处理，处理完成后通过视图响应给客户端\n",
    "      return render(request, 'Book/booklist.html', context)\n",
    "    ```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##### 创建模板\n",
    "\n",
    "- 读取上下文数据\n",
    "- 构造网页html文档 : 书籍信息以列表样式展示\n",
    "\n",
    "    ```html\n",
    "    <!DOCTYPE html>\n",
    "    <html lang=\"en\">\n",
    "    <head>\n",
    "        <meta charset=\"UTF-8\">\n",
    "        <title>书籍列表信息</title>\n",
    "    </head>\n",
    "    <body>\n",
    "\n",
    "    <ul>\n",
    "        {% for book in books %}\n",
    "            <li>{{ book.name }}</li>\n",
    "        {% endfor %}\n",
    "    </ul>\n",
    "\n",
    "    </body>\n",
    "    </html>\n",
    "    ```\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "##### 配置URLconf\n",
    "\n",
    "- 进入`应用`中的`urls.py`文件\n",
    "\n",
    "    ```python\n",
    "    from django.conf.urls import url\n",
    "    from book.views import index,bookList\n",
    "\n",
    "    urlpatterns = [\n",
    "        url(r'^$',index),\n",
    "        # 匹配书籍列表信息的URL,调用对应的bookList视图\n",
    "        url(r'^booklist/$',bookList)\n",
    "    ]\n",
    "    ```\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 配置文件\n",
    "\n",
    "### BASE_DIR\n",
    "\n",
    "```\n",
    "BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))\n",
    "```\n",
    "\n",
    "当前工程的根目录，Django会依此来定位工程内的相关文件，我们也可以使用该参数来构造文件路径。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### DEBUG\n",
    "\n",
    "调试模式，创建工程后初始值为**True**，即默认工作在调试模式下。\n",
    "\n",
    "作用：\n",
    "\n",
    "- 修改代码文件，程序自动重启\n",
    "\n",
    "- Django程序出现异常时，向前端显示详细的错误追踪信息\n",
    "\n",
    "- 而非调试模式下，仅返回Server Error (500)\n",
    "\n",
    "**注意：部署线上运行的Django不要运行在调式模式下，记得修改DEBUG=False和ALLOW_HOSTS。**\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "\n",
    "### 本地语言与时区\n",
    "\n",
    "Django支持本地化处理，即显示语言与时区支持本地化。\n",
    "\n",
    "本地化是将显示的语言、时间等使用本地的习惯，这里的本地化就是进行中国化，中国大陆地区使用**简体中文**，时区使用**亚洲/上海**时区，注意这里不使用北京时区表示。\n",
    "\n",
    "初始化的工程默认语言和时区为英语和UTC标准时区\n",
    "\n",
    "```python\n",
    "LANGUAGE_CODE = 'en-us'  # 语言\n",
    "TIME_ZONE = 'UTC'  # 时区# 时区\n",
    "```\n",
    "\n",
    "将语言和时区修改为中国大陆信息\n",
    "\n",
    "```python\n",
    "LANGUAGE_CODE = 'zh-Hans'\n",
    "TIME_ZONE = 'Asia/Shanghai'\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 静态文件\n",
    "\n",
    "项目中的CSS、图片、js都是静态文件。一般会将静态文件放到一个单独的目录中，以方便管理。在html页面中调用时，也需要指定静态文件的路径，Django中提供了一种解析的方式配置静态文件路径。静态文件可以放在项目根目录下，也可以放在应用的目录下，由于有些静态文件在项目中是通用的，所以推荐放在项目的根目录下，方便管理。\n",
    "\n",
    "为了提供静态文件，需要配置两个参数：\n",
    "\n",
    "- **STATICFILES_DIRS**存放查找静态文件的目录\n",
    "- **STATIC_URL**访问静态文件的URL前缀\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "### 示例\n",
    "\n",
    "1） 在项目根目录下创建static目录来保存静态文件。\n",
    "\n",
    "2） 在bookmanager/settings.py中修改静态文件的两个参数为\n",
    "\n",
    "```python\n",
    "STATIC_URL = '/static/'\n",
    "STATICFILES_DIRS = [\n",
    "    os.path.join(BASE_DIR, 'static'),\n",
    "]\n",
    "```\n",
    "\n",
    "3）此时在static添加的任何静态文件都可以使用网址**/static/文件在static中的路径**来访问了。\n",
    "\n",
    "例如，我们向static目录中添加一个index.html文件，在浏览器中就可以使用127.0.0.1:8000/static/index.html来访问。\n",
    "\n",
    "或者我们在static目录中添加了一个子目录和文件book/detail.html，在浏览器中就可以使用127.0.0.1:8000/static/book/detail.html来访问。\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## App应用配置\n",
    "\n",
    "在每个应用目录中都包含了apps.py文件，用于保存该应用的相关信息。\n",
    "\n",
    "在创建应用时，Django会向apps.py文件中写入一个该应用的配置类，如\n",
    "\n",
    "```python\n",
    "from django.apps import AppConfig\n",
    "\n",
    "\n",
    "class BookConfig(AppConfig):\n",
    "    name = 'book'\n",
    "```\n",
    "\n",
    "我们将此类添加到工程settings.py中的INSTALLED_APPS列表中，表明注册安装具备此配置属性的应用。\n",
    "\n",
    "- **AppConfig.name**属性表示这个配置类是加载到哪个应用的，每个配置类必须包含此属性，默认自动生成。\n",
    "\n",
    "- **AppConfig.verbose_name**属性用于设置该应用的直观可读的名字，此名字在Django提供的Admin管理站点中会显示，如\n",
    "\n",
    "  ```python\n",
    "  from django.apps import AppConfig\n",
    "  \n",
    "  class UsersConfig(AppConfig):\n",
    "      name = 'book'\n",
    "      verbose_name = '图书管理'\n",
    "  ```\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 模型\n",
    "\n",
    "### 重点\n",
    "\n",
    "- **模型配置**\n",
    "- **数据的增删改**\n",
    "  - 增:`book = BookInfo() book.save()` 和`BookInfo.objects.create()`\n",
    "  - 删:`book.delete()` 和`BookInfo.objects.get().delete()`\n",
    "  - 改:`book.name='xxx' book.save()` 和 `BookInfo.objects.get().update(name=xxx)`\n",
    "- **数据的查询**\n",
    "  - 基础查询\n",
    "  - F对象和Q对象\n",
    "  - 关联查询\n",
    "  - 查询集QuerySet"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 项目准备\n",
    "\n",
    "## 创建项目\n",
    "\n",
    "```\n",
    "django-admin startproject bookmanager\n",
    "```\n",
    "\n",
    "## 创建应用\n",
    "\n",
    "```\n",
    "python manager.py startapp book\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 更换python解释器：按需选择\n",
    "\n",
    "```\n",
    "# 进入指定虚拟环境\n",
    "which python\n",
    "```\n",
    "> 创建项目对应的虚拟环境 指定对应的python解释器"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "## 安装应用\n",
    "\n",
    "```Python\n",
    "INSTALLED_APPS = [\n",
    "  'django.contrib.admin',\n",
    "  'django.contrib.auth',\n",
    "  'django.contrib.contenttypes',\n",
    "  'django.contrib.sessions',\n",
    "  'django.contrib.messages',\n",
    "  'django.contrib.staticfiles',\n",
    "  #添加子应用\n",
    "  'book.apps.BookConfig'\n",
    "]\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "## 本地化\n",
    "\n",
    "```python\n",
    "#设置中文\n",
    "LANGUAGE_CODE = 'zh-Hans'\n",
    "#亚洲上海时区\n",
    "TIME_ZONE = 'Asia/Shanghai'\n",
    "```\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 模板路径\n",
    "\n",
    "  - 在应用同级目录下,创建`templates`模板文件夹\n",
    "\n",
    "    ```python\n",
    "    TEMPLATES = [\n",
    "        {\n",
    "            'BACKEND': 'django.template.backends.django.DjangoTemplates',\n",
    "            'DIRS': [os.path.join(BASE_DIR,'templates')],\n",
    "            'APP_DIRS': True,\n",
    "            'OPTIONS': {\n",
    "                'context_processors': [\n",
    "                    'django.template.context_processors.debug',\n",
    "                    'django.template.context_processors.request',\n",
    "                    'django.contrib.auth.context_processors.auth',\n",
    "                    'django.contrib.messages.context_processors.messages',\n",
    "                ],\n",
    "            },\n",
    "        },\n",
    "    ]\n",
    "    ```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "## 项目中匹配urls\n",
    "\n",
    "  - 正则 : 路径只要不是`admin/`就算匹配成功。并包含到应用中的`urls.py`\n",
    "\n",
    "```python\n",
    "from django.conf.urls import url,include\n",
    "from django.contrib import admin\n",
    "\n",
    "urlpatterns = [\n",
    "    url(r'^admin/', admin.site.urls),\n",
    "    #正则为：只要不是 admin/ 就算匹配成功\n",
    "    url(r'^',include('book.urls'))\n",
    "]\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "## 应用中匹配`urls.py`\n",
    "\n",
    "  - 应用中创建 `urls.py`\n",
    "\n",
    "  - 正则 : 路径中包含`booklist/`，就调用视图中对应的`bookList`函数\n",
    "\n",
    "```python\n",
    "from django.conf.urls import url\n",
    "from book.views import bookList\n",
    "\n",
    "urlpatterns = [\n",
    "    url(r'^booklist/',bookList) # 匹配书籍列表信息的URL,调用对应的bookList视图\n",
    "]\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "## 准备视图\n",
    "\n",
    "  ```python\n",
    "  # 定义视图：提供书籍列表信息\n",
    "  def bookList(request):\n",
    "  \n",
    "      return HttpResponse('OK!')\n",
    "  ```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "## 开启服务器, 测试项目\n",
    "\n",
    "  ```\n",
    "   # 进入项目文件中, 开启项目对应的服务器\n",
    "   python manage.py runserver\n",
    "  \n",
    "   # 浏览器中输入网址\n",
    "   http://127.0.0.1:8000/booklist/\n",
    "  ```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 配置\n",
    "\n",
    "在settings.py中保存了数据库的连接配置信息，Django默认初始配置使用**sqlite**数据库。\n",
    "\n",
    "```python\n",
    "DATABASES = {\n",
    "    'default': {\n",
    "        'ENGINE': 'django.db.backends.sqlite3',\n",
    "        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),\n",
    "    }\n",
    "}\n",
    "```\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "## 使用**MySQL**数据库首先需要安装驱动程序\n",
    "\n",
    "   ```python\n",
    "   pip install PyMySQL\n",
    "   ```\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 在Django的工程同名子目录的__init__.py文件中添加如下语句\n",
    "\n",
    "   ```python\n",
    "   import pymysql\n",
    "   \n",
    "   pymysql.install_as_MySQLdb()\n",
    "   ```\n",
    "\n",
    "   作用是让Django的ORM能以mysqldb的方式来调用PyMySQL。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "## 修改**DATABASES**配置信息\n",
    "\n",
    "   ```python\n",
    "   DATABASES = {\n",
    "       'default': {\n",
    "           'ENGINE': 'django.db.backends.mysql',\n",
    "           'HOST': '127.0.0.1',  # 数据库主机\n",
    "           'PORT': 3306,  # 数据库端口\n",
    "           'USER': 'root',  # 数据库用户名\n",
    "           'PASSWORD': 'root',  # 数据库用户密码\n",
    "           'NAME': 'book'  # 数据库名字\n",
    "       }\n",
    "   }\n",
    "   ```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "## 在MySQL中创建数据库\n",
    "\n",
    "   ```\n",
    "   create database book charset=utf8;\n",
    "   ```\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 定义模型类\n",
    "\n",
    "- 模型类被定义在\"应用/models.py\"文件中。\n",
    "- 模型类必须继承自Model类，位于包django.db.models中。\n",
    "\n",
    "> 接下来首先以\"图书-人物\"管理为例进行演示。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "##  定义\n",
    "\n",
    "在models.py 文件中定义模型类。\n",
    "\n",
    "```python\n",
    "from django.db import models\n",
    "\n",
    "# Create your models here.\n",
    "# 准备书籍列表信息的模型类\n",
    "class BookInfo(models.Model):\n",
    "    # 创建字段，字段类型...\n",
    "    name = models.CharField(max_length=20, verbose_name='名称')\n",
    "    pub_date = models.DateField(verbose_name='发布日期',null=True)\n",
    "    readcount = models.IntegerField(default=0, verbose_name='阅读量')\n",
    "    commentcount = models.IntegerField(default=0, verbose_name='评论量')\n",
    "    is_delete = models.BooleanField(default=False, verbose_name='逻辑删除')\n",
    "\n",
    "    class Meta:\n",
    "        db_table = 'bookinfo'  # 指明数据库表名\n",
    "        verbose_name = '图书'  # 在admin站点中显示的名称\n",
    "\n",
    "    def __str__(self):\n",
    "        \"\"\"定义每个数据对象的显示信息\"\"\"\n",
    "        return self.name\n",
    "\n",
    "# 准备人物列表信息的模型类\n",
    "class PeopleInfo(models.Model):\n",
    "    GENDER_CHOICES = (\n",
    "        (0, 'male'),\n",
    "        (1, 'female')\n",
    "    )\n",
    "    name = models.CharField(max_length=20, verbose_name='名称')\n",
    "    gender = models.SmallIntegerField(choices=GENDER_CHOICES, default=0, verbose_name='性别')\n",
    "    description = models.CharField(max_length=200, null=True, verbose_name='描述信息')\n",
    "    book = models.ForeignKey(BookInfo, on_delete=models.CASCADE, verbose_name='图书')  # 外键\n",
    "    is_delete = models.BooleanField(default=False, verbose_name='逻辑删除')\n",
    "\n",
    "    class Meta:\n",
    "        db_table = 'peopleinfo'\n",
    "        verbose_name = '人物信息'\n",
    "\n",
    "    def __str__(self):\n",
    "        return self.name\n",
    "```\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "### 数据库表名\n",
    "\n",
    "模型类如果未指明表名，Django默认以**小写app应用名_小写模型类名**为数据库表名。\n",
    "\n",
    "可通过**db_table**指明数据库表名。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "### 关于主键\n",
    "\n",
    "django会为表创建自动增长的主键列，每个模型只能有一个主键列，如果使用选项设置某属性为主键列后django不会再创建自动增长的主键列。\n",
    "\n",
    "默认创建的主键列属性为id，可以使用pk代替，pk全拼为primary key。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 属性命名限制\n",
    "\n",
    "- 不能是python的保留关键字。\n",
    "\n",
    "- 不允许使用连续的下划线，这是由django的查询方式决定的。\n",
    "\n",
    "- 定义属性时需要指定字段类型，通过字段类型的参数指定选项，语法如下：\n",
    "\n",
    "  ```\n",
    "  属性=models.字段类型(选项)\n",
    "  ```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 字段类型\n",
    "\n",
    "| 类型             | 说明                                                         |\n",
    "| :--------------- | :----------------------------------------------------------- |\n",
    "| AutoField        | 自动增长的IntegerField，通常不用指定，不指定时Django会自动创建属性名为id的自动增长属性 |\n",
    "| BooleanField     | 布尔字段，值为True或False                                    |\n",
    "| NullBooleanField | 支持Null、True、False三种值                                  |\n",
    "| CharField        | 字符串，参数max_length表示最大字符个数                       |\n",
    "| TextField        | 大文本字段，一般超过4000个字符时使用                         |\n",
    "| IntegerField     | 整数                                                         |\n",
    "| DecimalField     | 十进制浮点数， 参数max_digits表示总位数， 参数decimal_places表示小数位数 |\n",
    "| FloatField       | 浮点数                                                       |\n",
    "| DateField        | 日期， 参数auto_now表示每次保存对象时，自动设置该字段为当前时间，用于\"最后一次修改\"的时间戳，它总是使用当前日期，默认为False； 参数auto_now_add表示当对象第一次被创建时自动设置当前时间，用于创建的时间戳，它总是使用当前日期，默认为False; 参数auto_now_add和auto_now是相互排斥的，组合将会发生错误 |\n",
    "| TimeField        | 时间，参数同DateField                                        |\n",
    "| DateTimeField    | 日期时间，参数同DateField                                    |\n",
    "| FileField        | 上传文件字段                                                 |\n",
    "| ImageField       | 继承于FileField，对上传的内容进行校验，确保是有效的图片      |\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 选项\n",
    "\n",
    "| 选项        | 说明                                                         |\n",
    "| :---------- | :----------------------------------------------------------- |\n",
    "| null        | 如果为True，表示允许为空，默认值是False                      |\n",
    "| blank       | 如果为True，则该字段允许为空白，默认值是False                |\n",
    "| db_column   | 字段的名称，如果未指定，则使用属性的名称                     |\n",
    "| db_index    | 若值为True, 则在表中会为此字段创建索引，默认值是False        |\n",
    "| default     | 默认                                                         |\n",
    "| primary_key | 若为True，则该字段会成为模型的主键字段，默认值是False，一般作为AutoField的选项使用 |\n",
    "| unique      | 如果为True, 这个字段在表中必须有唯一值，默认值是False        |\n",
    "\n",
    "**null是数据库范畴的概念，blank是表单验证范畴的**\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "### 外键\n",
    "\n",
    "在设置外键时，需要通过**on_delete**选项指明主表删除数据时，对于外键引用表数据如何处理，在django.db.models中包含了可选常量：\n",
    "\n",
    "- **CASCADE**级联，删除主表数据时连通一起删除外键表中数据\n",
    "- **PROTECT**保护，通过抛出**ProtectedError**异常，来阻止删除主表中被外键应用的数据\n",
    "- **SET_NULL**设置为NULL，仅在该字段null=True允许为null时可用\n",
    "- **SET_DEFAULT**设置为默认值，仅在该字段设置了默认值时可用\n",
    "- **SET()**设置为特定值或者调用特定方法\n",
    "- **DO_NOTHING**不做任何操作，如果数据库前置指明级联性，此选项会抛出**IntegrityError**异常\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "## 迁移\n",
    "\n",
    "将模型类同步到数据库中。\n",
    "\n",
    "### 生成迁移文件\n",
    "\n",
    "```\n",
    "python manage.py makemigrations\n",
    "```\n",
    "\n",
    "### 同步到数据库中\n",
    "\n",
    "```\n",
    "python manage.py migrate\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "##  添加测试数据\n",
    "\n",
    "```python\n",
    "insert into bookinfo(name, pub_date, readcount,commentcount, is_delete) values\n",
    "('射雕英雄传', '1980-5-1', 12, 34, 0),\n",
    "('天龙八部', '1986-7-24', 36, 40, 0),\n",
    "('笑傲江湖', '1995-12-24', 20, 80, 0),\n",
    "('雪山飞狐', '1987-11-11', 58, 24, 0);\n",
    "insert into peopleinfo(name, gender, book_id, description, is_delete)  values\n",
    "    ('郭靖', 1, 1, '降龙十八掌', 0),\n",
    "    ('黄蓉', 0, 1, '打狗棍法', 0),\n",
    "    ('黄药师', 1, 1, '弹指神通', 0),\n",
    "    ('欧阳锋', 1, 1, '蛤蟆功', 0),\n",
    "    ('梅超风', 0, 1, '九阴白骨爪', 0),\n",
    "    ('乔峰', 1, 2, '降龙十八掌', 0),\n",
    "    ('段誉', 1, 2, '六脉神剑', 0),\n",
    "    ('虚竹', 1, 2, '天山六阳掌', 0),\n",
    "    ('王语嫣', 0, 2, '神仙姐姐', 0),\n",
    "    ('令狐冲', 1, 3, '独孤九剑', 0),\n",
    "    ('任盈盈', 0, 3, '弹琴', 0),\n",
    "    ('岳不群', 1, 3, '华山剑法', 0),\n",
    "    ('东方不败', 0, 3, '葵花宝典', 0),\n",
    "    ('胡斐', 1, 4, '胡家刀法', 0),\n",
    "    ('苗若兰', 0, 4, '黄衣', 0),\n",
    "    ('程灵素', 0, 4, '医术', 0),\n",
    "    ('袁紫衣', 0, 4, '六合拳', 0);\n",
    "```\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## shell工具和查看MySQL数据库日志\n",
    "\n",
    "###  shell工具\n",
    "\n",
    "Django的manage工具提供了**shell**命令，帮助我们配置好当前工程的运行环境（如连接好数据库等），以便可以直接在终端中执行测试python语句。\n",
    "\n",
    "通过如下命令进入shell\n",
    "\n",
    "```\n",
    "python manage.py shell\n",
    "```\n",
    "\n",
    "\n",
    "导入两个模型类，以便后续使用\n",
    "\n",
    "```\n",
    "from book.models import BookInfo,PeopleInfo\n",
    "```\n",
    "\n",
    "### 查看MySQL数据库日志\n",
    "\n",
    "查看mysql数据库日志可以查看对数据库的操作记录。 mysql日志文件默认没有产生，需要做如下配置：\n",
    "\n",
    "```\n",
    "sudo vi /etc/mysql/mysql.conf.d/mysqld.cnf\n",
    "```\n",
    "\n",
    "\n",
    "把68，69行前面的#去除，然后保存并使用如下命令重启mysql服务。\n",
    "\n",
    "```\n",
    "sudo service mysql restart\n",
    "```\n",
    "\n",
    "使用如下命令打开mysql日志文件。\n",
    "\n",
    "```\n",
    "tail -f /var/log/mysql/mysql.log  # 可以实时查看数据库的日志内容\n",
    "# 如提示需要sudo权限，执行\n",
    "# sudo tail -f /var/log/mysql/mysql.log\n",
    "```\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 数据库操作\n",
    "\n",
    "##  增加\n",
    "\n",
    "增加数据有两种方法。\n",
    "\n",
    "**1）save**\n",
    "\n",
    "通过创建模型类对象，执行对象的save()方法保存到数据库\n",
    "\n",
    "\n",
    "**2）create**\n",
    "\n",
    "通过模型类.objects.create()保存。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "##  修改\n",
    "\n",
    "修改更新有两种方法\n",
    "\n",
    "**1）save**\n",
    "\n",
    "**修改模型类对象的属性，然后执行save()方法**\n",
    "\n",
    "\n",
    "**2）update**\n",
    "\n",
    "**使用模型类.objects.filter().update()**，会返回受影响的行数\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "##  删除\n",
    "\n",
    "删除有两种方法\n",
    "\n",
    "**1）模型类对象delete**\n",
    "\n",
    "**2）模型类.objects.filter().delete()**\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 基础条件查询\n",
    "\n",
    "###  基本查询\n",
    "\n",
    "**get**查询单一结果，如果不存在会抛出**模型类.DoesNotExist**异常。\n",
    "\n",
    "**all**查询多个结果。\n",
    "\n",
    "**count**查询结果数量。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "### 过滤查询\n",
    "\n",
    "实现SQL中的where功能，包括\n",
    "\n",
    "- **filter**过滤出多个结果\n",
    "- **exclude**排除掉符合条件剩下的结果\n",
    "- **get**过滤单一结果\n",
    "\n",
    "对于过滤条件的使用，上述三个方法相同，故仅以**filter**进行讲解。\n",
    "\n",
    "过滤条件的表达语法如下：\n",
    "\n",
    "```\n",
    "属性名称__比较运算符=值\n",
    "\n",
    "# 属性名称和比较运算符间使用两个下划线，所以属性名不能包括多个下划线\n",
    "查询编号为1的图书\n",
    "查询书名包含'湖'的图书\n",
    "查询书名以'部'结尾的图书\n",
    "查询书名为空的图书\n",
    "查询编号为1或3或5的图书\n",
    "查询编号大于3的图书\n",
    "查询1980年发表的图书\n",
    "查询1990年1月1日后发表的图书\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "#### 相等\n",
    "\n",
    "**exact：表示判等。**\n",
    "\n",
    "例：查询编号为1的图书。\n",
    "\n",
    "```\n",
    "BookInfo.objects.filter(id__exact=1)\n",
    "可简写为：\n",
    "BookInfo.objects.filter(id=1)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "#### 模糊查询\n",
    "\n",
    "**contains：是否包含。**\n",
    "\n",
    "> 说明：如果要包含%无需转义，直接写即可。\n",
    "\n",
    "\n",
    "**startswith、endswith：以指定值开头或结尾。**\n",
    "\n",
    "\n",
    "> 以上运算符都区分大小写，在这些运算符前加上i表示不区分大小写，如iexact、icontains、istartswith、iendswith."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 空查询\n",
    "\n",
    "**isnull：是否为null。**\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "#### 范围查询\n",
    "\n",
    "**in：是否包含在范围内。**\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "#### 比较查询\n",
    "\n",
    "- **gt**大于 (greater then)\n",
    "- **gte**大于等于 (greater then equal)\n",
    "- **lt**小于 (less then)\n",
    "- **lte**小于等于 (less then equal)\n",
    "\n",
    "\n",
    "**不等于的运算符，使用exclude()过滤器。**\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 日期查询\n",
    "\n",
    "**year、month、day、week_day、hour、minute、second：对日期时间类型的属性进行运算。**\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## F和Q对象\n",
    "\n",
    "### F对象\n",
    "\n",
    "之前的查询都是对象的属性与常量值比较，两个属性怎么比较呢？ 答：使用F对象，被定义在django.db.models中。\n",
    "\n",
    "语法如下：\n",
    "\n",
    "```\n",
    "F(属性名)\n",
    "```\n",
    "\n",
    "可以在F对象上使用算数运算。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "### Q对象\n",
    "\n",
    "**多个过滤器逐个调用表示逻辑与关系，同sql语句中where部分的and关键字。**\n",
    "\n",
    "\n",
    "\n",
    "**如果需要实现逻辑或or的查询，需要使用Q()对象结合|运算符**，Q对象被义在django.db.models中。\n",
    "\n",
    "\n",
    "Q对象可以使用&、|连接，&表示逻辑与，|表示逻辑或。\n",
    "\n",
    "\n",
    "Q对象前可以使用~操作符，表示非not。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 聚合函数和排序函数\n",
    "\n",
    "###  聚合函数\n",
    "\n",
    "使用aggregate()过滤器调用聚合函数。聚合函数包括：**Avg**平均，**Count**数量，**Max**最大，**Min**最小，**Sum**求和，被定义在django.db.models中。\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "###  排序\n",
    "\n",
    "使用**order_by**对结果进行排序\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 关联查询\n",
    "\n",
    "```\n",
    "查询书籍为1的所有人物信息\n",
    "查询人物为1的书籍信息\n",
    "```\n",
    "\n",
    "由一到多的访问语法：\n",
    "\n",
    "一对应的模型类对象.多对应的模型类名小写_set 例：\n",
    "\n",
    "```\n",
    ">>> book = BookInfo.objects.get(id=1)\n",
    ">>> book.peopleinfo_set.all()\n",
    "<QuerySet [<PeopleInfo: 郭靖>, <PeopleInfo: 黄蓉>, <PeopleInfo: 黄药师>, <PeopleInfo: 欧阳锋>, <PeopleInfo: 梅超风>]>\n",
    "```\n",
    "\n",
    "由多到一的访问语法:\n",
    "\n",
    "多对应的模型类对象.多对应的模型类中的关系类属性名 例：\n",
    "\n",
    "```\n",
    "person = PeopleInfo.objects.get(id=1)\n",
    "person.book\n",
    "<BookInfo: 射雕英雄传>\n",
    "```\n",
    "\n",
    "访问一对应的模型类关联对象的id语法:\n",
    "\n",
    "多对应的模型类对象.关联类属性_id\n",
    "\n",
    "例：\n",
    "\n",
    "```\n",
    ">>> person = PeopleInfo.objects.get(id=1)\n",
    ">>> person.book_id\n",
    "1\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "### 关联过滤查询\n",
    "\n",
    "**由多模型类条件查询一模型类数据**:\n",
    "\n",
    "语法如下：\n",
    "\n",
    "```\n",
    "关联模型类名小写__属性名__条件运算符=值\n",
    "```\n",
    "\n",
    "> **注意：如果没有\"__运算符\"部分，表示等于。**\n",
    "\n",
    "```\n",
    "查询图书，要求图书人物为\"郭靖\"\n",
    "查询图书，要求图书中人物的描述包含\"八\"\n",
    "```\n",
    "\n",
    "例：\n",
    "\n",
    "查询图书，要求图书人物为\"郭靖\"\n",
    "\n",
    "```\n",
    ">>> book = BookInfo.objects.filter(peopleinfo__name='郭靖')\n",
    ">>> book\n",
    "<QuerySet [<BookInfo: 射雕英雄传>]>\n",
    "```\n",
    "\n",
    "查询图书，要求图书中人物的描述包含\"八\"\n",
    "\n",
    "```\n",
    ">>> book = BookInfo.objects.filter(peopleinfo__description__contains='八')\n",
    ">>> book\n",
    "<QuerySet [<BookInfo: 射雕英雄传>, <BookInfo: 天龙八部>]>\n",
    "```\n",
    "\n",
    "**由一模型类条件查询多模型类数据**:\n",
    "\n",
    "语法如下：\n",
    "\n",
    "```\n",
    "一模型类关联属性名__一模型类属性名__条件运算符=值\n",
    "```\n",
    "\n",
    "> **注意：如果没有\"__运算符\"部分，表示等于。**\n",
    "\n",
    "```\n",
    "查询书名为“天龙八部”的所有人物\n",
    "查询图书阅读量大于30的所有人物\n",
    "```\n",
    "\n",
    "例：\n",
    "\n",
    "查询书名为“天龙八部”的所有人物。\n",
    "\n",
    "```\n",
    ">>> people = PeopleInfo.objects.filter(book__name='天龙八部')\n",
    ">>> people\n",
    "<QuerySet [<PeopleInfo: 乔峰>, <PeopleInfo: 段誉>, <PeopleInfo: 虚竹>, <PeopleInfo: 王语嫣>]>\n",
    "```\n",
    "\n",
    "查询图书阅读量大于30的所有人物\n",
    "\n",
    "```\n",
    ">>> people = PeopleInfo.objects.filter(book__readcount__gt=30)\n",
    ">>> people\n",
    "<QuerySet [<PeopleInfo: 乔峰>, <PeopleInfo: 段誉>, <PeopleInfo: 虚竹>, <PeopleInfo: 王语嫣>, <PeopleInfo: 胡斐>, <PeopleInfo: 苗若兰>, <PeopleInfo: 程灵素>, <PeopleInfo: 袁紫衣>]>\n",
    "```\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 查询集QuerySet\n",
    "\n",
    "### 概念\n",
    "\n",
    "Django的ORM中存在查询集的概念。\n",
    "\n",
    "查询集，也称查询结果集、QuerySet，表示从数据库中获取的对象集合。\n",
    "\n",
    "当调用如下过滤器方法时，Django会返回查询集（而不是简单的列表）：\n",
    "\n",
    "- all()：返回所有数据。\n",
    "- filter()：返回满足条件的数据。\n",
    "- exclude()：返回满足条件之外的数据。\n",
    "- order_by()：对结果进行排序。\n",
    "\n",
    "对查询集可以再次调用过滤器进行过滤，如\n",
    "\n",
    "```\n",
    ">>> books = BookInfo.objects.filter(readcount__gt=30).order_by('pub_date')\n",
    ">>> books\n",
    "<QuerySet [<BookInfo: 天龙八部>, <BookInfo: 雪山飞狐>]>\n",
    "```\n",
    "\n",
    "也就意味着查询集可以含有零个、一个或多个过滤器。过滤器基于所给的参数限制查询的结果。\n",
    "\n",
    "**从SQL的角度讲，查询集与select语句等价，过滤器像where、limit、order by子句。**\n",
    "\n",
    "**判断某一个查询集中是否有数据**：\n",
    "\n",
    "- exists()：判断查询集中是否有数据，如果有则返回True，没有则返回False。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "###  两大特性\n",
    "\n",
    "#### 惰性执行\n",
    "\n",
    "创建查询集不会访问数据库，直到调用数据时，才会访问数据库，调用数据的情况包括迭代、序列化、与if合用\n",
    "\n",
    "例如，当执行如下语句时，并未进行数据库查询，只是创建了一个查询集books\n",
    "\n",
    "```\n",
    "books = BookInfo.objects.all()\n",
    "```\n",
    "\n",
    "继续执行遍历迭代操作后，才真正的进行了数据库的查询\n",
    "\n",
    "```\n",
    "for book in books:\n",
    "    print(book.name)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "#### 缓存\n",
    "\n",
    "使用同一个查询集，第一次使用时会发生数据库的查询，然后Django会把结果缓存下来，再次使用这个查询集时会使用缓存的数据，减少了数据库的查询次数。\n",
    "\n",
    "**情况一**：如下是两个查询集，无法重用缓存，每次查询都会与数据库进行一次交互，增加了数据库的负载。\n",
    "\n",
    "```\n",
    "from book.models import BookInfo\n",
    "\n",
    " [book.id for book in BookInfo.objects.all()]\n",
    "\n",
    " [book.id for book in BookInfo.objects.all()]\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "**情况二**：经过存储后，可以重用查询集，第二次使用缓存中的数据。\n",
    "\n",
    "```\n",
    "books=BookInfo.objects.all()\n",
    "\n",
    "[book.id for book in books]\n",
    "\n",
    "[book.id for book in books]\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "### 限制查询集\n",
    "\n",
    "可以对查询集进行取下标或切片操作，等同于sql中的limit和offset子句。\n",
    "\n",
    "> 注意：不支持负数索引。\n",
    "\n",
    "**对查询集进行切片后返回一个新的查询集，不会立即执行查询。**\n",
    "\n",
    "如果获取一个对象，直接使用[0]，等同于[0:1].get()，但是如果没有数据，[0]引发IndexError异常，[0:1].get()如果没有数据引发DoesNotExist异常。\n",
    "\n",
    "示例：获取第1、2项，运行查看。\n",
    "\n",
    "```\n",
    ">>> books = BookInfo.objects.all()[0:2]\n",
    ">>> books\n",
    "<QuerySet [<BookInfo: 射雕英雄传>, <BookInfo: 天龙八部>]>\n",
    "```\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 管理器Manager\n",
    "\n",
    "管理器是Django的模型进行数据库操作的接口，Django应用的每个模型类都拥有至少一个管理器。\n",
    "\n",
    "我们在通过模型类的**objects**属性提供的方法操作数据库时，即是在使用一个管理器对象objects。当没有为模型类定义管理器时，Django会为每一个模型类生成一个名为objects的管理器，它是**models.Manager**类的对象。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "## 自定义管理器\n",
    "\n",
    "我们可以自定义管理器，并应用到我们的模型类上。\n",
    "\n",
    "**注意：一旦为模型类指明自定义的过滤器后，Django不再生成默认管理对象objects。**\n",
    "\n",
    "自定义管理器类主要用于两种情况：\n",
    "\n",
    "- 准备工作：把`bookinfo`表中的一条记录`is_delete`字段修改成`True`\n",
    "\n",
    "\n",
    "- 问题：逻辑删除字段为`True`的那条记录依然会被查询出来"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "### 修改原始查询集，重写all()方法。\n",
    "\n",
    "#### 打开book/models.py文件，定义类BookInfoManager\n",
    "\n",
    "```python\n",
    "class BookInfoManager(models.Manager):\n",
    "\n",
    "    def all(self):\n",
    "        #默认查询未删除的图书信息\n",
    "        #调用父类的成员语法为：super().方法名\n",
    "        return super().filter(is_delete=False)\n",
    "```\n",
    "\n",
    "#### 在模型类BookInfo中定义管理器\n",
    "\n",
    "```python\n",
    "class BookInfo(models.Model):\n",
    "    ...\n",
    "    books = BookInfoManager()\n",
    "```\n",
    "\n",
    "#### 使用方法\n",
    "\n",
    "```python\n",
    ">>> books = BookInfo.books.all()\n",
    ">>> books\n",
    "<QuerySet [<BookInfo: 天龙八部>, <BookInfo: 笑傲江湖>, <BookInfo: 雪山飞狐>, <BookInfo: python入门>]>\n",
    "```\n",
    "\n",
    "#### 视图方法\n",
    "\n",
    "```python\n",
    "# 书籍列表信息视图\n",
    "  def bookList(request):\n",
    "    # 查询数据库书籍列表数据\n",
    "    # books = BookInfo.objects.all()\n",
    "    books = BookInfo.books.all()\n",
    "    # 构造上下文\n",
    "    context = {'books':books}\n",
    "    # 数据交给模板处理，处理完成后通过视图响应给客户端\n",
    "    return render(request, 'Book/booklist.html', context)\n",
    "```\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "### 在管理器类中补充定义新的方法\n",
    "\n",
    "#### 打开book/models.py文件，定义方法create。\n",
    "\n",
    "```python\n",
    "class BookInfoManager(models.Manager):\n",
    "\n",
    "    def create_book(self,name,pub_date):\n",
    "        # 创建模型类对象self.model可以获得模型类\n",
    "        book = self.model()\n",
    "        book.name = name\n",
    "        book.pub_date = pub_date\n",
    "        book.save()\n",
    "        return book\n",
    "```\n",
    "\n",
    "#### 为模型类BookInfo定义管理器books语法如下\n",
    "\n",
    "```python\n",
    "class BookInfo(models.Model):\n",
    "      ...\n",
    "    books = BookInfoManager()\n",
    "```\n",
    "\n",
    "#### 调用语法如下：\n",
    "\n",
    "```python\n",
    ">>> from book.models import BookInfo\n",
    ">>> book = BookInfo.books.create_book('python高级','2010-1-1')\n",
    ">>> book\n",
    "<BookInfo: python高级>\n",
    "```\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 视图\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 视图介绍和项目准备\n",
    "\n",
    "### 视图介绍\n",
    "\n",
    "- 视图就是`应用`中`views.py`文件中的函数\n",
    "\n",
    "- 视图的第一个参数必须为`HttpRequest对象`，还可能包含下参数如\n",
    "\n",
    "  - 通过正则表达式组获取的位置参数\n",
    "  - 通过正则表达式组获得的关键字参数\n",
    "\n",
    "- 视图必须返回一个`HttpResponse对象`或`子对象`作为响应\n",
    "\n",
    "  - 子对象： `JsonResponse` `HttpResponseRedirect`\n",
    "\n",
    "- 视图负责接受Web请求`HttpRequest`，进行逻辑处理，返回Web响应`HttpResponse`给请求者\n",
    "\n",
    "  - 响应内容可以是`HTML内容`，`404错误`，`重定向`，`json数据`...\n",
    "\n",
    "- 视图处理过程如下图：\n",
    "\n",
    "\n",
    "> 使用视图时需要进行两步操作，两步操作不分先后\n",
    ">\n",
    "> 1. 配置`URLconf`\n",
    "> 2. 在`应用/views.py`中定义视图\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "### 项目准备\n",
    "> 创建项目+创建应用+安装应用+配置模板路径+本地化+mysql数据库+URLconf+视图\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "#### mysql数据库使用之前的book\n",
    "\n",
    "  ```python\n",
    "  DATABASES = {\n",
    "      'default': {\n",
    "          'ENGINE': 'django.db.backends.mysql',\n",
    "          'HOST': '127.0.0.1', # 数据库主机\n",
    "          'PORT': 3306, # 数据库端口\n",
    "          'USER': 'root', # 数据库用户名\n",
    "          'PASSWORD': 'mysql', # 数据库用户密码\n",
    "          'NAME': 'book' # 数据库名字\n",
    "      }\n",
    "  }\n",
    "  ```\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "#### URLconf\n",
    "\n",
    "  - `settings.py`中：指定url配置\n",
    "\n",
    "    ```python\n",
    "    ROOT_URLCONF = 'bookmanager.urls'\n",
    "    ```\n",
    "\n",
    "  - 项目中`urls.py`：只要不是`admin/`就匹配成功，包含到应用中的`urls.py`\n",
    "\n",
    "    ```python\n",
    "    from django.conf.urls import url,include\n",
    "    from django.contrib import admin\n",
    "    \n",
    "    urlpatterns = [\n",
    "         url(r'^admin/', include(admin.site.urls)),\n",
    "    \n",
    "         # 只要不是‘admin/’就匹配成功，包含到应用中的urls.py\n",
    "         url(r'^', include('book.urls')),\n",
    "     ]\n",
    "    ```\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "#### 应用中`urls.py`：\n",
    "\n",
    "匹配`testproject/`成功就调用`views`中的`testproject`函数，测试项目逻辑\n",
    "\n",
    "  ```python\n",
    "    from django.conf.urls import url\n",
    "    import views\n",
    "  \n",
    "    urlpatterns = [\n",
    "        # 匹配`testproject/`成功就调用`views`中的`testproject`函数\n",
    "        url(r'^testproject/', views.testproject),\n",
    "    ]\n",
    "  ```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "#### 视图：测试项目逻辑\n",
    "\n",
    "  ```python\n",
    "  from django.http import HttpResponse\n",
    "  \n",
    "    # 测试项目逻辑\n",
    "    def testproject(request):\n",
    "        return HttpResponse('测试项目逻辑')\n",
    "  ```\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "#### 在models.py 文件中定义模型类\n",
    "\n",
    "```python\n",
    "from django.db import models\n",
    "\n",
    "# Create your models here.\n",
    "# 准备书籍列表信息的模型类\n",
    "class BookInfo(models.Model):\n",
    "    # 创建字段，字段类型...\n",
    "    name = models.CharField(max_length=20, verbose_name='名称')\n",
    "    pub_date = models.DateField(verbose_name='发布日期',null=True)\n",
    "    readcount = models.IntegerField(default=0, verbose_name='阅读量')\n",
    "    commentcount = models.IntegerField(default=0, verbose_name='评论量')\n",
    "    is_delete = models.BooleanField(default=False, verbose_name='逻辑删除')\n",
    "\n",
    "    class Meta:\n",
    "        db_table = 'bookinfo'  # 指明数据库表名\n",
    "        verbose_name = '图书'  # 在admin站点中显示的名称\n",
    "\n",
    "    def __str__(self):\n",
    "        \"\"\"定义每个数据对象的显示信息\"\"\"\n",
    "        return self.name\n",
    "\n",
    "# 准备人物列表信息的模型类\n",
    "class PeopleInfo(models.Model):\n",
    "    GENDER_CHOICES = (\n",
    "        (0, 'male'),\n",
    "        (1, 'female')\n",
    "    )\n",
    "    name = models.CharField(max_length=20, verbose_name='名称')\n",
    "    gender = models.SmallIntegerField(choices=GENDER_CHOICES, default=0, verbose_name='性别')\n",
    "    description = models.CharField(max_length=200, null=True, verbose_name='描述信息')\n",
    "    book = models.ForeignKey(BookInfo, on_delete=models.CASCADE, verbose_name='图书')  # 外键\n",
    "    is_delete = models.BooleanField(default=False, verbose_name='逻辑删除')\n",
    "\n",
    "    class Meta:\n",
    "        db_table = 'peopleinfo'\n",
    "        verbose_name = '人物信息'\n",
    "\n",
    "    def __str__(self):\n",
    "        return self.name\n",
    "```\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "- **生成迁移文件**\n",
    "\n",
    "```\n",
    "python manage.py makemigrations\n",
    "```\n",
    "\n",
    "- **同步到数据库中**\n",
    "\n",
    "```\n",
    "python manage.py migrate\n",
    "```\n",
    "\n",
    "- **添加测试数据**\n",
    "\n",
    "```\n",
    "insert into bookinfo(name, pub_date, readcount,commentcount, is_delete) values\n",
    "('射雕英雄传', '1980-5-1', 12, 34, 0),\n",
    "('天龙八部', '1986-7-24', 36, 40, 0),\n",
    "('笑傲江湖', '1995-12-24', 20, 80, 0),\n",
    "('雪山飞狐', '1987-11-11', 58, 24, 0);\n",
    "insert into peopleinfo(name, gender, book_id, description, is_delete)  values\n",
    "    ('郭靖', 1, 1, '降龙十八掌', 0),\n",
    "    ('黄蓉', 0, 1, '打狗棍法', 0),\n",
    "    ('黄药师', 1, 1, '弹指神通', 0),\n",
    "    ('欧阳锋', 1, 1, '蛤蟆功', 0),\n",
    "    ('梅超风', 0, 1, '九阴白骨爪', 0),\n",
    "    ('乔峰', 1, 2, '降龙十八掌', 0),\n",
    "    ('段誉', 1, 2, '六脉神剑', 0),\n",
    "    ('虚竹', 1, 2, '天山六阳掌', 0),\n",
    "    ('王语嫣', 0, 2, '神仙姐姐', 0),\n",
    "    ('令狐冲', 1, 3, '独孤九剑', 0),\n",
    "    ('任盈盈', 0, 3, '弹琴', 0),\n",
    "    ('岳不群', 1, 3, '华山剑法', 0),\n",
    "    ('东方不败', 0, 3, '葵花宝典', 0),\n",
    "    ('胡斐', 1, 4, '胡家刀法', 0),\n",
    "    ('苗若兰', 0, 4, '黄衣', 0),\n",
    "    ('程灵素', 0, 4, '医术', 0),\n",
    "    ('袁紫衣', 0, 4, '六合拳', 0);\n",
    "```\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## URLconf\n",
    "\n",
    "- 浏览者通过在浏览器的地址栏中输入网址请求网站\n",
    "- 对于Django开发的网站，由哪一个视图进行处理请求，是由url匹配找到的\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "### 配置URLconf\n",
    "\n",
    "####  `settings.py`中\n",
    "\n",
    "  - 指定url配置\n",
    "\n",
    "    ```\n",
    "    ROOT_URLCONF = '项目.urls'\n",
    "    ```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "#### 项目中`urls.py`\n",
    "\n",
    "  - 匹配成功后，包含到应用的`urls.py`\n",
    "\n",
    "    ```\n",
    "    url(正则, include('应用.urls'))\n",
    "    ```\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "#### 应用中`urls.py`\n",
    "\n",
    "  - 匹配成功后，调用`views.py`对应的函数\n",
    "\n",
    "    ```\n",
    "    url(正则, views.函数名)\n",
    "    ```\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "#### 提示\n",
    "\n",
    "  ```\n",
    "    1. 正则部分推荐使用 r，表示字符串不转义，这样在正则\n",
    "    表达式中使用 \\ 只写一个就可以\n",
    "  \n",
    "    2. 不能在开始加反斜杠，推荐在结束加反斜杠\n",
    "        正确：path/\n",
    "        正确：path\n",
    "        错误：/path\n",
    "        错误：/path/\n",
    "  \n",
    "    3. 请求的url被看做是一个普通的python字符串，进行匹配时不包括域名、get或post参数\n",
    "        3.1 如请求地址如下：\n",
    "            http://127.0.0.1:8000/18/?a=10\n",
    "        3.2 去掉域名和参数部分后，只剩下如下部分与正则匹配\n",
    "            18/\n",
    "  ```\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "**说明：**\n",
    "\n",
    "虽然路由结尾带/能带来上述好处，但是却违背了HTTP中URL表示资源位置路径的设计理念。\n",
    "\n",
    "是否结尾带/以所属公司定义风格为准。\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 路由命名与reverse反解析（逆向）\n",
    "\n",
    "###  路由命名\n",
    "\n",
    "在定义路由的时候，可以为路由命名，方便查找特定视图的具体路径信息。\n",
    "\n",
    "1) 在使用include函数定义路由时，可以使用namespace参数定义路由的命名空间，如\n",
    "\n",
    "```\n",
    "url(r'^',include('book.urls',namespace='book'))\n",
    "```\n",
    "\n",
    "命名空间表示，凡是book.urls中定义的路由，均属于namespace指明的book名下。\n",
    "\n",
    "**命名空间的作用：避免不同应用中的路由使用了相同的名字发生冲突，使用命名空间区别开。**\n",
    "\n",
    "2) 在定义普通路由时，可以使用name参数指明路由的名字，如\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "urlpatterns = [\n",
    "    url(r'^$', index),\n",
    "    # 匹配书籍列表信息的URL,调用对应的bookList视图\n",
    "    url(r'^booklist/$', bookList, name='index'),\n",
    "    url(r'^testproject/$', views.testproject, name='test'),\n",
    "]\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "###  reverse反解析\n",
    "\n",
    "使用reverse函数，可以根据路由名称，返回具体的路径，如：\n",
    "\n",
    "```\n",
    "from django.core.urlresolvers import reverse\n",
    "#或者\n",
    "from django.urls import reverse\n",
    "\n",
    "def testproject(request):\n",
    "\n",
    "    return HttpResponse(\"OK\")\n",
    "\n",
    "# 定义视图：提供书籍列表信息\n",
    "def bookList(request):\n",
    "\n",
    "    url = reverse('book:test')\n",
    "    print(url)\n",
    "    return HttpResponse('index')\n",
    "```\n",
    "\n",
    "- 对于未指明namespace的，reverse(路由name)\n",
    "- 对于指明namespace的，reverse(命名空间namespace:路由name)\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## HttpRequest对象\n",
    "\n",
    "回想一下，利用HTTP协议向服务器传参有几种途径？\n",
    "\n",
    "- 提取URL的特定部分，如/weather/beijing/2018，可以在服务器端的路由中用正则表达式截取；\n",
    "- 查询字符串（query string)，形如key1=value1&key2=value2；\n",
    "- 请求体（body）中发送的数据，比如表单数据、json、xml；\n",
    "- 在http报文的头（header）中。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "### URL路径参数\n",
    "\n",
    "- 如果想从URL中获取值，需要在正则表达式中使用`分组`，\n",
    "\n",
    "- 获取值分为两种方式\n",
    "\n",
    "  - 位置参数\n",
    "    - 参数的位置不能错\n",
    "  - 关键字参数\n",
    "    - 参数的位置可以变，跟关键字保持一致即可\n",
    "\n",
    "- 注意：两种参数的方式不要混合使用，在一个正则表达式中只能使用一种参数方式\n",
    "\n",
    "- 分别使用以上两种获取URL值的方式提取出\n",
    "\n",
    "  ```\n",
    "  18 188\n",
    "  ```\n",
    "\n",
    "  - `http://127.0.0.1:8000/18/188/`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "#### 位置参数\n",
    "\n",
    "- 应用中`urls.py`\n",
    "\n",
    "  ```\n",
    "   url(r'^(\\d+)/(\\d+)/$', views.index),\n",
    "  ```\n",
    "\n",
    "- 视图中函数: 参数的位置不能错\n",
    "\n",
    "  ```\n",
    "  def index(request, value1, value2):\n",
    "        # 构造上下文\n",
    "        context = {'v1':value1, 'v2':value2}\n",
    "        return render(request, 'Book/index.html', context)\n",
    "  ```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "#### 关键字参数\n",
    "\n",
    "- 应用中`urls.py`\n",
    "\n",
    "  - 其中`?P<value1>`部分表示为这个参数定义的名称为`value1`\n",
    "\n",
    "  - 可以是其它名称，起名要做到见名知意\n",
    "\n",
    "    ```\n",
    "    url(r'^(?P<value1>\\d+)/(?P<value2>\\d+)/$', views.index),\n",
    "    ```\n",
    "\n",
    "- 视图中函数: 参数的位置可以变，跟关键字保持一致即可\n",
    "\n",
    "  ```\n",
    "  def index(request, value2, value1):\n",
    "        # 构造上下文\n",
    "        context = {'v1':value1, 'v2':value2}\n",
    "        return render(request, 'Book/index.html', context)\n",
    "  ```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "### Django中的QueryDict对象\n",
    "\n",
    "HttpRequest对象的属性GET、POST都是QueryDict类型的对象\n",
    "\n",
    "与python字典不同，QueryDict类型的对象用来处理同一个键带有多个值的情况\n",
    "\n",
    "- 方法get()：根据键获取值\n",
    "\n",
    "  如果一个键同时拥有多个值将获取最后一个值\n",
    "\n",
    "  如果键不存在则返回None值，可以设置默认值进行后续处理\n",
    "\n",
    "  ```\n",
    "  get('键',默认值)\n",
    "  ```\n",
    "\n",
    "- 方法getlist()：根据键获取值，值以列表返回，可以获取指定键的所有值\n",
    "\n",
    "  如果键不存在则返回空列表[]，可以设置默认值进行后续处理\n",
    "\n",
    "  ```\n",
    "  getlist('键',默认值)\n",
    "  ```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "###  查询字符串Query String\n",
    "\n",
    "获取请求路径中的查询字符串参数（形如?k1=v1&k2=v2），可以通过request.GET属性获取，返回QueryDict对象。\n",
    "\n",
    "```\n",
    "# /get/?a=1&b=2&a=3\n",
    "\n",
    "def get(request):\n",
    "    a = request.GET.get('a')\n",
    "    b = request.GET.get('b')\n",
    "    alist = request.GET.getlist('a')\n",
    "    print(a)  # 3\n",
    "    print(b)  # 2\n",
    "    print(alist)  # ['1', '3']\n",
    "    return HttpResponse('OK')\n",
    "```\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**重要：查询字符串不区分请求方式，即假使客户端进行POST方式的请求，依然可以通过request.GET获取请求中的查询字符串数据。**\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "### 请求体\n",
    "\n",
    "请求体数据格式不固定，可以是表单类型字符串，可以是JSON字符串，可以是XML字符串，应区别对待。\n",
    "\n",
    "可以发送请求体数据的请求方式有**POST**、**PUT**、**PATCH**、**DELETE**。\n",
    "\n",
    "**Django默认开启了CSRF防护**，会对上述请求方式进行CSRF防护验证，在测试时可以关闭CSRF防护机制，方法为在settings.py文件中注释掉CSRF中间件"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "#### 表单类型 Form Data\n",
    "\n",
    "前端发送的表单类型的请求体数据，可以通过request.POST属性获取，返回QueryDict对象。\n",
    "\n",
    "```python\n",
    "def post(request):\n",
    "    a = request.POST.get('a')\n",
    "    b = request.POST.get('b')\n",
    "    alist = request.POST.getlist('a')\n",
    "    print(a)\n",
    "    print(b)\n",
    "    print(alist)\n",
    "    return HttpResponse('OK')\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "#### 非表单类型 Non-Form Data\n",
    "\n",
    "非表单类型的请求体数据，Django无法自动解析，可以通过**request.body**属性获取最原始的请求体数据，自己按照请求体格式（JSON、XML等）进行解析。**request.body返回bytes类型。**\n",
    "\n",
    "例如要获取请求体中的如下JSON数据\n",
    "\n",
    "```\n",
    "{\"a\": 1, \"b\": 2}\n",
    "```\n",
    "\n",
    "可以进行如下方法操作：\n",
    "\n",
    "```python\n",
    "import json\n",
    "\n",
    "def post_json(request):\n",
    "    json_str = request.body\n",
    "    json_str = json_str.decode()  # python3.6 无需执行此步\n",
    "    req_data = json.loads(json_str)\n",
    "    print(req_data['a'])\n",
    "    print(req_data['b'])\n",
    "    return HttpResponse('OK')\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "### 请求头\n",
    "\n",
    "可以通过**request.META**属性获取请求头headers中的数据，**request.META为字典类型**。\n",
    "\n",
    "常见的请求头如：\n",
    "\n",
    "- `CONTENT_LENGTH`– The length of the request body (as a string).\n",
    "- `CONTENT_TYPE`– The MIME type of the request body.\n",
    "- `HTTP_ACCEPT`– Acceptable content types for the response.\n",
    "- `HTTP_ACCEPT_ENCODING`– Acceptable encodings for the response.\n",
    "- `HTTP_ACCEPT_LANGUAGE`– Acceptable languages for the response.\n",
    "- `HTTP_HOST`– The HTTP Host header sent by the client.\n",
    "- `HTTP_REFERER`– The referring page, if any.\n",
    "- `HTTP_USER_AGENT`– The client’s user-agent string.\n",
    "- `QUERY_STRING`– The query string, as a single (unparsed) string.\n",
    "- `REMOTE_ADDR`– The IP address of the client.\n",
    "- `REMOTE_HOST`– The hostname of the client.\n",
    "- `REMOTE_USER`– The user authenticated by the Web server, if any.\n",
    "- `REQUEST_METHOD`– A string such as`\"GET\"`or`\"POST\"`.\n",
    "- `SERVER_NAME`– The hostname of the server.\n",
    "- `SERVER_PORT`– The port of the server (as a string).\n",
    "\n",
    "具体使用如:\n",
    "\n",
    "```python\n",
    "def get_headers(request):\n",
    "    print(request.META['CONTENT_TYPE'])\n",
    "    return HttpResponse('OK')\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "### 其他常用HttpRequest对象属性\n",
    "\n",
    "- **method**：一个字符串，表示请求使用的HTTP方法，常用值包括：'GET'、'POST'。\n",
    "- **user：请求的用户对象。**\n",
    "- path：一个字符串，表示请求的页面的完整路径，不包含域名和参数部分。\n",
    "- encoding：一个字符串，表示提交的数据的编码方式。\n",
    "  - 如果为None则表示使用浏览器的默认设置，一般为utf-8。\n",
    "  - 这个属性是可写的，可以通过修改它来修改访问表单数据使用的编码，接下来对属性的任何访问将使用新的encoding值。\n",
    "- FILES：一个类似于字典的对象，包含所有的上传文件。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## HttpResponse对象\n",
    "\n",
    "视图在接收请求并处理后，必须返回HttpResponse对象或子对象。HttpRequest对象由Django创建，HttpResponse对象由开发人员创建。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "### HttpResponse\n",
    "\n",
    "可以使用**django.http.HttpResponse**来构造响应对象。\n",
    "\n",
    "```\n",
    "HttpResponse(content=响应体, content_type=响应体数据类型, status=状态码)\n",
    "```\n",
    "\n",
    "也可通过HttpResponse对象属性来设置响应体、响应体数据类型、状态码：\n",
    "\n",
    "- content：表示返回的内容。\n",
    "- status_code：返回的HTTP响应状态码。\n",
    "- content_type：指定返回数据的的MIME类型。\n",
    "\n",
    "响应头可以直接将HttpResponse对象当做字典进行响应头键值对的设置"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "### HttpResponse子类\n",
    "\n",
    "Django提供了一系列HttpResponse的子类，可以快速设置状态码\n",
    "\n",
    "- HttpResponseRedirect 301\n",
    "- HttpResponsePermanentRedirect 302\n",
    "- HttpResponseNotModified 304\n",
    "- HttpResponseBadRequest 400\n",
    "- HttpResponseNotFound 404\n",
    "- HttpResponseForbidden 403\n",
    "- HttpResponseNotAllowed 405\n",
    "- HttpResponseGone 410\n",
    "- HttpResponseServerError 500\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "### JsonResponse\n",
    "\n",
    "若要返回json数据，可以使用JsonResponse来构造响应对象，作用：\n",
    "\n",
    "- 帮助我们将数据转换为json字符串\n",
    "- 设置响应头**Content-Type**为**application/json**\n",
    "\n",
    "```python\n",
    "from django.http import JsonResponse\n",
    "\n",
    "def response(request):\n",
    "    return JsonResponse({'city': 'beijing', 'subject': 'python'})\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "### redirect重定向\n",
    "\n",
    "```python\n",
    "from django.shortcuts import redirect\n",
    "\n",
    "def response(request):\n",
    "    return redirect('/get_header')\n",
    "```\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 状态保持\n",
    "\n",
    "- 浏览器请求服务器是无状态的。\n",
    "- **无状态**：指一次用户请求时，浏览器、服务器无法知道之前这个用户做过什么，每次请求都是一次新的请求。\n",
    "- **无状态原因**：浏览器与服务器是使用Socket套接字进行通信的，服务器将请求结果返回给浏览器之后，会关闭当前的Socket连接，而且服务器也会在处理页面完毕之后销毁页面对象。\n",
    "- 有时需要保持下来用户浏览的状态，比如用户是否登录过，浏览过哪些商品等\n",
    "- 实现状态保持主要有两种方式：\n",
    "  - 在客户端存储信息使用`Cookie`\n",
    "  - 在服务器端存储信息使用`Session`\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Cookie\n",
    "\n",
    "Cookie，有时也用其复数形式Cookies，指某些网站为了辨别用户身份、进行session跟踪而储存在用户本地终端上的数据（通常经过加密）。Cookie最早是网景公司的前雇员Lou Montulli在1993年3月的发明。Cookie是由服务器端生成，发送给User-Agent（一般是浏览器），浏览器会将Cookie的key/value保存到某个目录下的文本文件内，下次请求同一网站时就发送该Cookie给服务器（前提是浏览器设置为启用cookie）。Cookie名称和值可以由服务器端开发自己定义，这样服务器可以知道该用户是否是合法用户以及是否需要重新登录等。服务器可以利用Cookies包含信息的任意性来筛选并经常性维护这些信息，以判断在HTTP传输中的状态。Cookies最典型记住用户名。\n",
    "\n",
    "Cookie是存储在浏览器中的一段纯文本信息，建议不要存储敏感信息如密码，因为电脑上的浏览器可能被其它人使用。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "#### Cookie的特点\n",
    "\n",
    "- Cookie以键值对的格式进行信息的存储。\n",
    "- Cookie基于域名安全，不同域名的Cookie是不能互相访问的，\n",
    "- 当浏览器请求某网站时，会将浏览器存储的跟网站相关的所有Cookie信息提交给网站服务器。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "##### 1 设置Cookie\n",
    "\n",
    "可以通过**HttpResponse**对象中的**set_cookie**方法来设置cookie。\n",
    "\n",
    "```\n",
    "HttpResponse.set_cookie(cookie名, value=cookie值, max_age=cookie有效期)\n",
    "```\n",
    "\n",
    "- **max_age**单位为秒，默认为**None** 。如果是临时cookie，可将max_age设置为None。\n",
    "\n",
    "示例：\n",
    "\n",
    "```python\n",
    "def cookie(request):\n",
    "    response = HttpResponse('ok')\n",
    "    response.set_cookie('cookie1', 'python1')  # 临时cookie\n",
    "    response.set_cookie('cookie2', 'python2', max_age=3600)  # 有效期一小时\n",
    "    return response\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "##### 2 读取Cookie\n",
    "\n",
    "可以通过**HttpResponse**对象的**COOKIES**属性来读取本次请求携带的cookie值。**request.COOKIES为字典类型**。\n",
    "\n",
    "```python\n",
    "def cookie(request):\n",
    "    cookie1 = request.COOKIES.get('cookie1')\n",
    "    print(cookie1)\n",
    "    return HttpResponse('OK')\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "##### 3 删除Cookie\n",
    "\n",
    "可以通过**HttpResponse**对象中的delete_cookie方法来删除。\n",
    "\n",
    "```python\n",
    "response.delete_cookie('cookie2')\n",
    "```\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Session\n",
    "\n",
    "###  启用Session\n",
    "\n",
    "**Django项目默认启用Session。**\n",
    "\n",
    "可以在settings.py文件中查看\n",
    "\n",
    "```python\n",
    "    'django.contrib.sessions.middleware.SessionMiddleware',\n",
    "```\n",
    "如需禁用session，将上图中的session中间件注释掉即可。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "### 存储方式\n",
    "\n",
    "在settings.py文件中，可以设置session数据的存储方式，可以保存在数据库、本地缓存等。\n",
    "\n",
    "#### 数据库\n",
    "\n",
    "存储在数据库中，如下设置可以写，也可以不写，**这是默认存储方式**。\n",
    "\n",
    "```\n",
    "SESSION_ENGINE='django.contrib.sessions.backends.db'\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "如果存储在数据库中，需要在项INSTALLED_APPS中安装Session应用。\n",
    "```python\n",
    "INSTALLED_APPS = [\n",
    "    'django.contrib.admin',\n",
    "    'django.contrib.auth',\n",
    "    'django.contrib.contenttypes',\n",
    "    'django.contrib.sessions',  # 安装Session应用\n",
    "    'django.contrib.messages',\n",
    "    'django.contrib.staticfiles',\n",
    "    # 添加子应用book\n",
    "    'first.apps.FirstConfig'\n",
    "]\n",
    "```\n",
    "\n",
    "可以在数据库中找到对应的表，由表结构可知，操作Session包括三个数据：键，值，过期时间。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 本地缓存\n",
    "\n",
    "存储在本机内存中，如果丢失则不能找回，比数据库的方式读写更快。\n",
    "\n",
    "```\n",
    "SESSION_ENGINE='django.contrib.sessions.backends.cache'\n",
    "```\n",
    "\n",
    "#### 混合存储\n",
    "\n",
    "优先从本机内存中存取，如果没有则从数据库中存取。\n",
    "\n",
    "```\n",
    "SESSION_ENGINE='django.contrib.sessions.backends.cached_db'\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "#### Redis\n",
    "\n",
    "在redis中保存session，需要引入第三方扩展，我们可以使用**django-redis**来解决。\n",
    "\n",
    "1） 安装扩展\n",
    "\n",
    "```\n",
    "pip install django-redis\n",
    "```\n",
    "\n",
    "2）配置\n",
    "\n",
    "在settings.py文件中做如下设置\n",
    "\n",
    "```python\n",
    "CACHES = {\n",
    "    'default': {\n",
    "        'BACKEND': 'django_redis.cache.RedisCache',\n",
    "        'LOCATION': 'redis://127.0.0.1:6379/1',\n",
    "        'OPTIONS': {\n",
    "            'CLIENT_CLASS': 'django_redis.client.DefaultClient',\n",
    "        }\n",
    "    }\n",
    "}\n",
    "SESSION_ENGINE = 'django.contrib.sessions.backends.cache'\n",
    "SESSION_CACHE_ALIAS = 'default'\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "##### 注意\n",
    "\n",
    "如果redis的ip地址不是本地回环127.0.0.1，而是其他地址，访问Django时，可能出现Redis连接错误，如下：\n",
    "\n",
    "\n",
    "解决方法：\n",
    "\n",
    "- 修改redis的配置文件，添加特定ip地址。\n",
    "\n",
    "- 打开redis的配置文件\n",
    "\n",
    "- 在配置项进行修改\n",
    "\n",
    "- 重新启动redis服务\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "### Session操作\n",
    "\n",
    "通过HttpRequest对象的session属性进行会话的读写操作。\n",
    "\n",
    "1） 以键值对的格式写session。\n",
    "\n",
    "```\n",
    "request.session['键']=值\n",
    "```\n",
    "\n",
    "2）根据键读取值。\n",
    "\n",
    "```\n",
    "request.session.get('键',默认值)\n",
    "```\n",
    "\n",
    "3）清除所有session，在存储中删除值部分。\n",
    "\n",
    "```\n",
    "request.session.clear()\n",
    "```\n",
    "\n",
    "4）清除session数据，在存储中删除session的整条数据。\n",
    "\n",
    "```\n",
    "request.session.flush()\n",
    "```\n",
    "\n",
    "5）删除session中的指定键及值，在存储中只删除某个键及对应的值。\n",
    "\n",
    "```\n",
    "del request.session['键']\n",
    "```\n",
    "\n",
    "6）设置session的有效期\n",
    "\n",
    "```\n",
    "request.session.set_expiry(value)\n",
    "```\n",
    "\n",
    "- 如果value是一个整数，session将在value秒没有活动后过期。\n",
    "- 如果value为0，那么用户session的Cookie将在用户的浏览器关闭时过期。\n",
    "- 如果value为None，那么session有效期将采用系统默认值， **默认为两周**，可以通过在settings.py中设置**SESSION_COOKIE_AGE**来设置全局默认值。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 类视图\n",
    "\n",
    "思考：一个视图，是否可以处理两种逻辑？比如get和post请求逻辑。\n",
    "\n",
    "\n",
    "### 注册视图处理get和post请求\n",
    "\n",
    "以函数的方式定义的视图称为**函数视图**，函数视图便于理解。但是遇到一个视图对应的路径提供了多种不同HTTP请求方式的支持时，便需要在一个函数中编写不同的业务逻辑，代码可读性与复用性都不佳。\n",
    "\n",
    "```python\n",
    "def register(request):\n",
    "    \"\"\"处理注册\"\"\"\n",
    "\n",
    "    # 获取请求方法，判断是GET/POST请求\n",
    "    if request.method == 'GET':\n",
    "        # 处理GET请求，返回注册页面\n",
    "        return render(request, 'register.html')\n",
    "    else:\n",
    "        # 处理POST请求，实现注册逻辑\n",
    "        return HttpResponse('这里实现注册逻辑')\n",
    "```\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 类视图使用\n",
    "\n",
    "在Django中也可以使用类来定义一个视图，称为**类视图**。\n",
    "\n",
    "使用类视图可以将视图对应的不同请求方式以类中的不同方法来区别定义。如下所示\n",
    "\n",
    "```python\n",
    "from django.views.generic import View\n",
    "\n",
    "class RegisterView(View):\n",
    "    \"\"\"类视图：处理注册\"\"\"\n",
    "\n",
    "    def get(self, request):\n",
    "        \"\"\"处理GET请求，返回注册页面\"\"\"\n",
    "        return render(request, 'register.html')\n",
    "\n",
    "    def post(self, request):\n",
    "        \"\"\"处理POST请求，实现注册逻辑\"\"\"\n",
    "        return HttpResponse('这里实现注册逻辑')\n",
    "```\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "类视图的好处：\n",
    "\n",
    "- **代码可读性好**\n",
    "- **类视图相对于函数视图有更高的复用性** ， 如果其他地方需要用到某个类视图的某个特定逻辑，直接继承该类视图即可\n",
    "\n",
    "定义类视图需要继承自Django提供的父类**View**，可使用`from django.views.generic import View`或者`from django.views.generic.base import View`导入，定义方式如上所示。\n",
    "\n",
    "**配置路由时，使用类视图的**`as_view()`**方法来添加**。\n",
    "\n",
    "```python\n",
    "urlpatterns = [\n",
    "    # 视图函数：注册\n",
    "    # url(r'^register/$', views.register, name='register'),\n",
    "    # 类视图：注册\n",
    "    url(r'^register/$', views.RegisterView.as_view(), name='register'),\n",
    "]\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "### 类视图原理\n",
    "\n",
    "```python\n",
    "@classonlymethod\n",
    "    def as_view(cls, **initkwargs):\n",
    "        \"\"\"\n",
    "        Main entry point for a request-response process.\n",
    "        \"\"\"\n",
    "        ...省略代码...\n",
    "\n",
    "        def view(request, *args, **kwargs):\n",
    "            self = cls(**initkwargs)\n",
    "            if hasattr(self, 'get') and not hasattr(self, 'head'):\n",
    "                self.head = self.get\n",
    "            self.request = request\n",
    "            self.args = args\n",
    "            self.kwargs = kwargs\n",
    "            # 调用dispatch方法，按照不同请求方式调用不同请求方法\n",
    "            return self.dispatch(request, *args, **kwargs)\n",
    "\n",
    "        ...省略代码...\n",
    "\n",
    "        # 返回真正的函数视图\n",
    "        return view\n",
    "\n",
    "\n",
    "    def dispatch(self, request, *args, **kwargs):\n",
    "        # Try to dispatch to the right method; if a method doesn't exist,\n",
    "        # defer to the error handler. Also defer to the error handler if the\n",
    "        # request method isn't on the approved list.\n",
    "        if request.method.lower() in self.http_method_names:\n",
    "            handler = getattr(self, request.method.lower(), self.http_method_not_allowed)\n",
    "        else:\n",
    "            handler = self.http_method_not_allowed\n",
    "        return handler(request, *args, **kwargs)\n",
    "```\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 类视图使用装饰器\n",
    "\n",
    "为类视图添加装饰器，可以使用三种方法。\n",
    "\n",
    "为了理解方便，我们先来定义一个**为函数视图准备的装饰器**（在设计装饰器时基本都以函数视图作为考虑的被装饰对象），及一个要被装饰的类视图。\n",
    "\n",
    "```python\n",
    "def login_require(func_view):\n",
    "\n",
    "    def wrapper(request，*args,**kwargs):\n",
    "        if False:\n",
    "            return func_view(request,*args,**kwargs)\n",
    "        else:\n",
    "            return HttpResponse(\"您没有登陆\")\n",
    "    return wrapper\n",
    "\n",
    "\n",
    "class CenterView(View):\n",
    "\n",
    "    def get(self,request):\n",
    "        return HttpResponse(\"OK\")\n",
    "\n",
    "    def post(self,request):\n",
    "        return HttpResponse(\"OK\")\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "#### 在URL配置中装饰\n",
    "\n",
    "```python\n",
    "from book.views import login_require\n",
    "urlpatterns = [\n",
    "\n",
    "    url(r'^center/$',login_require(CenterView.as_view()),name='center'),\n",
    "]\n",
    "```\n",
    "\n",
    "此种方式最简单，但因装饰行为被放置到了url配置中，单看视图的时候无法知道此视图还被添加了装饰器，不利于代码的完整性，不建议使用。\n",
    "\n",
    "**此种方式会为类视图中的所有请求方法都加上装饰器行为**（因为是在视图入口处，分发请求方式前）。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 在类视图中装饰\n",
    "\n",
    "在类视图中使用为函数视图准备的装饰器时，不能直接添加装饰器，需要使用**method_decorator**将其转换为适用于类视图的装饰器。\n",
    "\n",
    "\n",
    "**method*****decorator装饰器还支持使用name参数指明被装饰的方法。***\n",
    "\n",
    "```python\n",
    "@method_decorator(login_require,name='dispatch')\n",
    "class CenterView(View):\n",
    "\n",
    "    def get(self,request):\n",
    "        return HttpResponse(\"OK\")\n",
    "\n",
    "    def post(self,request):\n",
    "        return HttpResponse(\"OK\")\n",
    "```\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "\n",
    "#### 构造Mixin扩展类\n",
    "\n",
    "使用面向对象多继承的特性。\n",
    "\n",
    "```python\n",
    "class LoginRequireMixin(object):\n",
    "    @classmethod\n",
    "    def as_view(cls,**kwargs):\n",
    "        view = super().as_view(**kwargs)\n",
    "        view = login_require(view)\n",
    "        return view\n",
    "\n",
    "class CenterView(LoginRequireMixin,View):\n",
    "\n",
    "    def get(self,request):\n",
    "        return HttpResponse(\"OK\")\n",
    "\n",
    "    def post(self,request):\n",
    "        return HttpResponse(\"OK\")\n",
    "```\n",
    "\n",
    "**使用Mixin扩展类，也会为类视图的所有请求方法都添加装饰行为。**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 中间件\n",
    "\n",
    "Django中的中间件是一个轻量级、底层的插件系统，可以介入Django的请求和响应处理过程，修改Django的输入或输出。中间件的设计为开发者提供了一种无侵入式的开发方式，增强了Django框架的健壮性。\n",
    "\n",
    "我们可以使用中间件，在Django处理视图的不同阶段对输入或输出进行干预。\n",
    "\n",
    "[中间件文档](https://docs.djangoproject.com/en/3.0/topics/http/middleware/)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##  中间件的定义方法\n",
    "\n",
    "定义一个中间件工厂函数，然后返回一个可以被调用的中间件。\n",
    "\n",
    "中间件工厂函数需要接收一个可以调用的get_response对象。\n",
    "\n",
    "返回的中间件也是一个可以被调用的对象，并且像视图一样需要接收一个request对象参数，返回一个response对象。\n",
    "\n",
    "```python\n",
    "def simple_middleware(get_response):\n",
    "    # 此处编写的代码仅在Django第一次配置和初始化的时候执行一次。\n",
    "\n",
    "    def middleware(request):\n",
    "        # 此处编写的代码会在每个请求处理视图前被调用。\n",
    "\n",
    "        response = get_response(request)\n",
    "\n",
    "        # 此处编写的代码会在每个请求处理视图之后被调用。\n",
    "\n",
    "        return response\n",
    "\n",
    "    return middleware\n",
    "```\n",
    "\n",
    "例如，在book应用中新建一个middleware.py文件，\n",
    "\n",
    "```python\n",
    "def my_middleware(get_response):\n",
    "    print('init 被调用')\n",
    "    def middleware(request):\n",
    "        print('before request 被调用')\n",
    "        response = get_response(request)\n",
    "        print('after response 被调用')\n",
    "        return response\n",
    "    return middleware\n",
    "```\n",
    "\n",
    "**定义好中间件后，需要在settings.py 文件中添加注册中间件**\n",
    "\n",
    "```python\n",
    "MIDDLEWARE = [\n",
    "    'django.middleware.security.SecurityMiddleware',\n",
    "    'django.contrib.sessions.middleware.SessionMiddleware',\n",
    "    'django.middleware.common.CommonMiddleware',\n",
    "    # 'django.middleware.csrf.CsrfViewMiddleware',\n",
    "    'django.contrib.auth.middleware.AuthenticationMiddleware',\n",
    "    'django.contrib.messages.middleware.MessageMiddleware',\n",
    "    'django.middleware.clickjacking.XFrameOptionsMiddleware',\n",
    "    'book.middleware.my_middleware',  # 添加中间件\n",
    "]\n",
    "```\n",
    "\n",
    "定义一个视图进行测试\n",
    "\n",
    "```python\n",
    "def middleware(request):\n",
    "    print('view 视图被调用')\n",
    "    return HttpResponse('OK')\n",
    "```\n",
    "\n",
    "\n",
    "\n",
    "**注意：Django运行在调试模式下，中间件init部分有可能被调用两次。**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "\n",
    "##  多个中间件的执行顺序\n",
    "\n",
    "- 在请求视图被处理**前**，中间件**由上至下**依次执行\n",
    "- 在请求视图被处理**后**，中间件**由下至上**依次执行\n",
    "\n",
    "\n",
    "示例：\n",
    "\n",
    "定义两个中间件\n",
    "\n",
    "```python\n",
    "def my_middleware(get_response):\n",
    "    print('init 被调用')\n",
    "    def middleware(request):\n",
    "        print('before request 被调用')\n",
    "        response = get_response(request)\n",
    "        print('after response 被调用')\n",
    "        return response\n",
    "    return middleware\n",
    "\n",
    "def my_middleware2(get_response):\n",
    "    print('init2 被调用')\n",
    "    def middleware(request):\n",
    "        print('before request 2 被调用')\n",
    "        response = get_response(request)\n",
    "        print('after response 2 被调用')\n",
    "        return response\n",
    "    return middleware\n",
    "```\n",
    "\n",
    "注册添加两个中间件\n",
    "\n",
    "```python\n",
    "MIDDLEWARE = [\n",
    "    'django.middleware.security.SecurityMiddleware',\n",
    "    'django.contrib.sessions.middleware.SessionMiddleware',\n",
    "    'django.middleware.common.CommonMiddleware',\n",
    "    # 'django.middleware.csrf.CsrfViewMiddleware',\n",
    "    'django.contrib.auth.middleware.AuthenticationMiddleware',\n",
    "    'django.contrib.messages.middleware.MessageMiddleware',\n",
    "    'django.middleware.clickjacking.XFrameOptionsMiddleware',\n",
    "    'users.middleware.my_middleware',  # 添加\n",
    "    'users.middleware.my_middleware2',  # 添加\n",
    "]\n",
    "```\n",
    "\n",
    "执行结果\n",
    "\n",
    "```\n",
    "init2 被调用\n",
    "init 被调用\n",
    "before request 被调用\n",
    "before request 2 被调用\n",
    "view 视图被调用\n",
    "after response 2 被调用\n",
    "after response 被调用\n",
    "```\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 模版"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##  配置\n",
    "\n",
    "在工程中创建模板目录templates。\n",
    "\n",
    "在settings.py配置文件中修改**TEMPLATES**配置项的DIRS值：\n",
    "\n",
    "```python\n",
    "TEMPLATES = [\n",
    "    {\n",
    "        'BACKEND': 'django.template.backends.django.DjangoTemplates',\n",
    "        'DIRS': [os.path.join(BASE_DIR, 'templates')],  # 此处修改\n",
    "        'APP_DIRS': True,\n",
    "        'OPTIONS': {\n",
    "            'context_processors': [\n",
    "                'django.template.context_processors.debug',\n",
    "                'django.template.context_processors.request',\n",
    "                'django.contrib.auth.context_processors.auth',\n",
    "                'django.contrib.messages.context_processors.messages',\n",
    "            ],\n",
    "        },\n",
    "    },\n",
    "]\n",
    "```\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "##  定义模板\n",
    "\n",
    "在templates目录中新建一个模板文件，如index.html\n",
    "\n",
    "```html\n",
    "<!DOCTYPE html>\n",
    "<html lang=\"en\">\n",
    "<head>\n",
    "    <meta charset=\"UTF-8\">\n",
    "    <title>Title</title>\n",
    "</head>\n",
    "<body>\n",
    "\n",
    "</body>\n",
    "</html>\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "##  模板渲染\n",
    "\n",
    "调用模板分为两步骤：\n",
    "\n",
    "1. 找到模板 loader.get_template(模板文件在模板目录中的相对路径) -> 返回模板对象\n",
    "2. 渲染模板 模板对象.render(context=None, request=None) -> 返回渲染后的html文本字符串 context 为模板变量字典，默认值为None request 为请求对象，默认值为None\n",
    "\n",
    "例如，定义一个视图\n",
    "\n",
    "```python\n",
    "from django.http import HttpResponse\n",
    "from django.template import loader\n",
    "\n",
    "def index(request):\n",
    "    # 1.获取模板\n",
    "    template=loader.get_template('index.html')\n",
    "\n",
    "    context={'city': '北京'}\n",
    "    # 2.渲染模板\n",
    "    return HttpResponse(template.render(context))\n",
    "```\n",
    "\n",
    "**Django提供了一个函数render可以简写上述代码。**\n",
    "\n",
    "render(request对象, 模板文件路径, 模板数据字典)\n",
    "\n",
    "```python\n",
    "from django.shortcuts import render\n",
    "\n",
    "def index(request):\n",
    "    context={'city': '北京'}\n",
    "    return render(request,'index.html',context)\n",
    "```\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "## 模板语法\n",
    "\n",
    "###  模板变量\n",
    "\n",
    "变量名必须由字母、数字、下划线（不能以下划线开头）和点组成。\n",
    "\n",
    "语法如下：`{{变量}}`\n",
    "模板变量可以使python的内建类型，也可以是对象。\n",
    "\n",
    "```python\n",
    "def index(request):\n",
    "    context = {\n",
    "        'city': '北京',\n",
    "        'adict': {\n",
    "            'name': '西游记',\n",
    "            'author': '吴承恩'\n",
    "        },\n",
    "        'alist': [1, 2, 3, 4, 5]\n",
    "    }\n",
    "    return render(request, 'index.html', context)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "###  模板语句\n",
    "\n",
    "**1）for循环：**  \n",
    "```python\n",
    "{% for item in list %}\n",
    "循环逻辑\n",
    "{{forloop.counter}}表示当前第几次循环  从1开始\n",
    "{%empty%} list为空或不存在时执行此逻辑\n",
    "{%endfor%}\n",
    "```\n",
    "**2）if条件：**  \n",
    "```python\n",
    "{% if ... %}\n",
    "逻辑1\n",
    "{% elif ... %}\n",
    "逻辑2\n",
    "{% else %}\n",
    "逻辑3\n",
    "{% endif %}\n",
    "```\n",
    "\n",
    "比较运算符如下：\n",
    "\n",
    "```\n",
    "==\n",
    "!=\n",
    "<\n",
    ">\n",
    "<=\n",
    ">=\n",
    "```\n",
    "\n",
    "布尔运算符如下：\n",
    "\n",
    "```\n",
    "and\n",
    "or\n",
    "not\n",
    "```\n",
    "\n",
    "**注意：运算符左右两侧不能紧挨变量或常量，必须有空格。**\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "### 过滤器\n",
    "\n",
    "语法如下:\n",
    "\n",
    "- 使用管道符号|来应用过滤器，用于进行计算、转换操作，可以使用在变量、标签中。\n",
    "\n",
    "- 如果过滤器需要参数，则使用冒号:传递参数。\n",
    "\n",
    "- ```\n",
    "  变量|过滤器:参数\n",
    "  ```\n",
    "\n",
    "列举几个如下：\n",
    "\n",
    "- **safe**，禁用转义，告诉模板这个变量是安全的，可以解释执行\n",
    "\n",
    "- **length**，长度，返回字符串包含字符的个数，或列表、元组、字典的元素个数。\n",
    "\n",
    "- **default**，默认值，如果变量不存在时则返回默认值。\n",
    "\n",
    "- ```\n",
    "  data|default:'默认值'\n",
    "  ```\n",
    "\n",
    "- **date**，日期，用于对日期类型的值进行字符串格式化，常用的格式化字符如下：\n",
    "\n",
    "  - Y表示年，格式为4位，y表示两位的年。\n",
    "\n",
    "  - m表示月，格式为01,02,12等。\n",
    "\n",
    "  - d表示日, 格式为01,02等。\n",
    "\n",
    "  - j表示日，格式为1,2等。\n",
    "\n",
    "  - H表示时，24进制，h表示12进制的时。\n",
    "\n",
    "  - i表示分，为0-59。\n",
    "\n",
    "  - s表示秒，为0-59。\n",
    "\n",
    "  - ```\n",
    "    value|date:\"Y年m月j日  H时i分s秒\"\n",
    "    ```\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "###  注释\n",
    "\n",
    "1. 单行注释语法如下：`{#...#}`\n",
    "\n",
    "2. 多行注释使用comment标签，语法如下：\n",
    ">{% comment %}\n",
    ">\n",
    ">...\n",
    ">\n",
    ">{% endcomment %}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "###  模板继承\n",
    "\n",
    "模板继承和类的继承含义是一样的，主要是为了提高代码重用，减轻开发人员的工作量。\n",
    "\n",
    "**父模板**\n",
    "\n",
    "如果发现在多个模板中某些内容相同，那就应该把这段内容定义到父模板中。\n",
    "\n",
    "标签block：用于在父模板中预留区域，留给子模板填充差异性的内容，名字不能相同。 为了更好的可读性，建议给endblock标签写上名字，这个名字与对应的block名字相同。父模板中也可以使用上下文中传递过来的数据。\n",
    "\n",
    "**子模板**\n",
    "\n",
    "标签extends：继承，写在子模板文件的第一行。\n",
    "\n",
    "> {% block 名称 %}\n",
    ">\n",
    "> 预留区域，可以编写默认内容，也可以没有\n",
    ">\n",
    "> {% endblock 名称 %}\n",
    "\n",
    "子模版不用填充父模版中的所有预留区域，如果子模版没有填充，则使用父模版定义的默认值。\n",
    "> {% extends \"父模版路径\" %}\n",
    "\n",
    "填充父模板中指定名称的预留区域。\n",
    "\n",
    "> {% block 名称 %}\n",
    ">\n",
    "> 实际填充内容\n",
    ">\n",
    "> {{ block.super }}用于获取父模版的block的内容\n",
    ">\n",
    "> {% endblock 名称 %}\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "# Admin站点\n",
    "\n",
    "假设我们要设计一个新闻网站，我们需要编写展示给用户的页面，网页上展示的新闻信息是从哪里来的呢？**是从数据库中查找到新闻的信息，\b然后把它展示在页面上**。但是我们的网站上的新闻每天都要更新，这就意味着对数据库的增、删、改、查操作，那么我们需要每天写sql语句\b操作数据库吗? 如果这样的话，是不是非常繁琐，所以我们可以设计一个页面，通过对这个页面的操作来实现对新闻数据库的增删改查操作。那么问题来了，老板说我们需要在建立一个新网站，是不是还要设计一个页面来实现对新网站数据库的增删改查操作，但是这样的页面具有一个很大的重复性，那有没有一种方法能够让我们很快的生成管理数据库表的页面呢？**有，那就是我们接下来要给大家讲的Django的后台管理**。Django能够根据定义的模型类自动地生成管理页面。\n",
    "\n",
    "使用Django的管理模块，需要按照如下步骤操作：\n",
    "\n",
    "1. 管理界面本地化\n",
    "2. 创建管理员\n",
    "3. 注册模型类\n",
    "4. 自定义管理页面"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "## 管理界面本地化\n",
    "\n",
    "在settings.py中设置语言和时区\n",
    "\n",
    "```python\n",
    "# 设置中文\n",
    "LANGUAGE_CODE = 'zh-Hans'\n",
    "# 亚洲上海时区\n",
    "TIME_ZONE = 'Asia/Shanghai'\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "\n",
    "## 创建超级管理员\n",
    "\n",
    "创建管理员的命令如下，按提示输入用户名、邮箱、密码。\n",
    "\n",
    "```\n",
    "python manage.py createsuperuser\n",
    "```\n",
    "\n",
    "打开浏览器，在地址栏中输入如下地址后回车。\n",
    "\n",
    "```\n",
    "http://127.0.0.1:8000/admin/\n",
    "```\n",
    "\n",
    "输入前面创建的用户名、密码完成登录。\n",
    "\n",
    "\n",
    "登录成功后界面如下，但是并没有我们自己应用模型的入口，接下来进行下面操作。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "##  注册模型类\n",
    "\n",
    "登录后台管理后，默认没有我们创建的应用中定义的模型类，需要在自己应用中的admin.py文件中注册，才可以在后台管理中看到，并进行增删改查操作。\n",
    "\n",
    "打开booktest/admin.py文件，编写如下代码：\n",
    "\n",
    "```python\n",
    "from django.contrib import admin\n",
    "# 导入模型\n",
    "from book.models import BookInfo,PeopleInfo\n",
    "# Register your models here.\n",
    "# 注册书籍模型\n",
    "admin.site.register(BookInfo)\n",
    "# 注册人物模型\n",
    "admin.site.register(PeopleInfo)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "到浏览器中刷新页面，可以看到模型类BookInfo和PeopleInfo的管理了。\n",
    "\n",
    "\n",
    "点击类名称\"BookInfo\"（图书）可以进入列表页，默认只有一列。\n",
    "\n",
    "\n",
    "在列表页中点击\"增加\"可以进入增加页，Django会根据模型类的不同，生成不同的表单控件，按提示填写表单内容后点击\"保存\"，完成数据创建，创建成功后返回列表页。\n",
    "\n",
    "\n",
    "在列表页中点击某行的第一列可以进入修改页。\n",
    "\n",
    "\n",
    "按照提示进行内容的修改，修改成功后进入列表页。在修改页点击“删除”可以删除一项。\n",
    "\n",
    "\n",
    "删除：在列表页勾选想要删除的复选框，可以删除多项。\n",
    "\n",
    "\n",
    "点击执行后进入确认页面，删除后回来列表页面。\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "##  定义与使用Admin管理类\n",
    "\n",
    "Django提供的Admin站点的展示效果可以通过自定义**ModelAdmin**类来进行控制。\n",
    "\n",
    "定义管理类需要继承自**admin.ModelAdmin**类，如下\n",
    "\n",
    "```python\n",
    "from django.contrib import admin\n",
    "\n",
    "class BookInfoAdmin(admin.ModelAdmin):\n",
    "    pass\n",
    "```\n",
    "\n",
    "使用管理类有两种方式：\n",
    "\n",
    "- 注册参数\n",
    "\n",
    "  ```python\n",
    "  admin.site.register(BookInfo,BookInfoAdmin)\n",
    "  ```\n",
    "\n",
    "- 装饰器\n",
    "\n",
    "  ```python\n",
    "  @admin.register(BookInfo)\n",
    "  class BookInfoAdmin(admin.ModelAdmin):\n",
    "      pass\n",
    "  ```\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 调整列表页展示\n",
    "\n",
    "### 页大小\n",
    "\n",
    "每页中显示多少条数据，默认为每页显示100条数据，属性如下：\n",
    "\n",
    "```\n",
    "list_per_page=100\n",
    "```\n",
    "\n",
    "1）打开booktest/admin.py文件，修改AreaAdmin类如下：\n",
    "\n",
    "```\n",
    "class BookInfoAdmin(admin.ModelAdmin):\n",
    "    list_per_page = 2\n",
    "```\n",
    "\n",
    "2）在浏览器中查看区域信息的列表页面"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "### \"操作选项\"的位置\n",
    "\n",
    "顶部显示的属性，设置为True在顶部显示，设置为False不在顶部显示，默认为True。\n",
    "\n",
    "```\n",
    "actions_on_top=True\n",
    "```\n",
    "\n",
    "底部显示的属性，设置为True在底部显示，设置为False不在底部显示，默认为False。\n",
    "\n",
    "```\n",
    "actions_on_bottom=False\n",
    "```\n",
    "\n",
    "- 打开booktest/admin.py文件，修改BookInfoAdmin类如下：\n",
    "\n",
    "```python\n",
    "class BookInfoAdmin(admin.ModelAdmin):\n",
    "    ...\n",
    "    actions_on_top = True\n",
    "    actions_on_bottom = True\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "### 列表中的列\n",
    "\n",
    "属性如下：\n",
    "\n",
    "```\n",
    "list_display=[模型字段1,模型字段2,...]\n",
    "```\n",
    "\n",
    "- 打开book/admin.py文件，修改BookInfoAdmin类如下：\n",
    "\n",
    "```python\n",
    "class BookInfoAdmin(admin.ModelAdmin):\n",
    "    ...\n",
    "    list_display = ['id','name']\n",
    "```\n",
    "\n",
    "**点击列头可以进行升序或降序排列。**\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "\n",
    "### 将方法作为列\n",
    "\n",
    "列可以是模型字段，还可以是模型方法，要求方法有返回值。\n",
    "\n",
    "**通过设置short_description属性，可以设置在admin站点中显示的列名。**\n",
    "\n",
    "- 打开book/models.py文件，修改BookInfo类如下：\n",
    "\n",
    "```\n",
    "class BookInfo (models.Model):\n",
    "\n",
    "    ...\n",
    "\n",
    "def bookname(self):\n",
    "        return '<<' + self.name + '>>'\n",
    "\n",
    "    bookname.short_description = '书名'  # 设置方法字段在admin中显示的标题\n",
    "```\n",
    "\n",
    "- 打开book/admin.py文件，修改BookInfoAdmin类如下：\n",
    "\n",
    "```\n",
    "class BookInfoAdmin (admin.ModelAdmin) :\n",
    "\n",
    "    ...\n",
    "   list_display = ['id','name','bookname']\n",
    "```\n",
    "\n",
    "\n",
    "方法列是不能排序的，如果需要排序需要为方法指定排序依据。\n",
    "\n",
    "```\n",
    "admin_order_field=模型类字段\n",
    "```\n",
    "\n",
    "- 打开book/models.py文件，修改BookInfo类如下：\n",
    "\n",
    "```\n",
    "class BookInfo(models.Model):\n",
    "\n",
    "    ...\n",
    "\n",
    "def bookname(self):\n",
    "        return '<<' + self.name + '>>'\n",
    "\n",
    "    bookname.short_description = '书名'  # 设置方法字段在admin中显示的标题\n",
    "    bookname.admin_order_field = 'name'\n",
    "```\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "### 关联对象\n",
    "\n",
    "无法直接访问关联对象的属性或方法，可以在模型类中封装方法，访问关联对象的成员。\n",
    "\n",
    "- 打开book/models.py文件，修改PeopleInfo类如下：\n",
    "\n",
    "```\n",
    "class PeopleInfo(models.Model):\n",
    "\n",
    "    ...\n",
    "\n",
    "    def readcount(self):\n",
    "        return self.book.readcount\n",
    "    readcount.short_description = '图书阅读量'\n",
    "```\n",
    "\n",
    "- 打开book/admin.py文件，修改PeopleInfoAdmin类如下：\n",
    "\n",
    "```\n",
    "@admin.register(PeopleInfo)\n",
    "class PeopleInfoAdmin(admin.ModelAdmin):\n",
    "\n",
    "    list_display = ['id','name','book','readcount']\n",
    "```\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "### 右侧栏过滤器\n",
    "\n",
    "属性如下，只能接收字段，会将对应字段的值列出来，用于快速过滤。一般用于有重复值的字段。\n",
    "\n",
    "```\n",
    "list_filter=[]\n",
    "```\n",
    "\n",
    "- 打开book/admin.py文件，修改PeopleInfoAdmin类如下：\n",
    "\n",
    "```\n",
    "@admin.register(PeopleInfo)\n",
    "class PeopleInfoAdmin(admin.ModelAdmin):\n",
    "\n",
    "    list_display = ['id','name','book','readcount']\n",
    "\n",
    "    list_filter = ['book','gender']\n",
    "```\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "### 搜索框\n",
    "\n",
    "属性如下，用于对指定字段的值进行搜索，支持模糊查询。列表类型，表示在这些字段上进行搜索。\n",
    "\n",
    "```\n",
    "search_fields=[]\n",
    "```\n",
    "- 打开book/admin.py文件，修改PeopleInfoAdmin类如下：\n",
    "\n",
    "```\n",
    "@admin.register(PeopleInfo)\n",
    "class PeopleInfoAdmin(admin.ModelAdmin):\n",
    "\n",
    "    list_display = ['id','name','book','readcount']\n",
    "\n",
    "    list_filter = ['book','gender']\n",
    "\n",
    "    search_fields = ['name']\n",
    "```\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 调整编辑页展示\n",
    "\n",
    "### 显示字段\n",
    "\n",
    "属性如下：\n",
    "\n",
    "```\n",
    "fields=[]\n",
    "```\n",
    "\n",
    "1）点击某行ID的链接，可以转到修改页面\n",
    "\n",
    "2）打开book/admin.py文件，修改BookInfoAdmin类如下：\n",
    "\n",
    "```\n",
    "class BookInfoAdmin(admin.ModelAdmin):\n",
    "    ...\n",
    "    fields = ['name', 'pub_date']\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "### 分组显示\n",
    "\n",
    "属性如下：\n",
    "\n",
    "```\n",
    "fieldsets=(\n",
    "    ('组1标题',{'fields':('字段1','字段2')}),\n",
    "    ('组2标题',{'fields':('字段3','字段4')}),\n",
    ")\n",
    "```\n",
    "\n",
    "1）打开book/admin.py文件，修改BookInfoAdmin类如下：\n",
    "\n",
    "```\n",
    "class BookInfoAdmin(admin.ModelAdmin):\n",
    "    ...\n",
    "    # fields = ['name', 'pub_date']\n",
    "    fieldsets = (\n",
    "        ('基本', {'fields': ['name', 'pub_date']}),\n",
    "        ('高级', {\n",
    "            'fields': ['readcount', 'commentcount'],\n",
    "            'classes': ('collapse',)  # 是否折叠显示\n",
    "        })\n",
    "    )\n",
    "```\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "\n",
    "### 关联对象\n",
    "\n",
    "在一对多的关系中，可以在一端的编辑页面中编辑多端的对象，嵌入多端对象的方式包括表格、块两种。\n",
    "\n",
    "- 类型InlineModelAdmin：表示在模型的编辑页面嵌入关联模型的编辑。\n",
    "- 子类TabularInline：以表格的形式嵌入。\n",
    "- 子类StackedInline：以块的形式嵌入。\n",
    "\n",
    "1）打开book/admin.py文件，创建PeopleInfoStackInLine类。\n",
    "\n",
    "```\n",
    "class PeopleInfoStackInLine(admin.StackedInline):\n",
    "    model = PeopleInfo #要关联的模型\n",
    "    extra = 2 #附加编辑的数量\n",
    "```\n",
    "\n",
    "2）打开book/admin.py文件，修改BookInfoAdmin类如下：\n",
    "\n",
    "```\n",
    "class BookInfoAdmin(admin.ModelAdmin):\n",
    "\n",
    "    ...\n",
    "    inlines = [PeopleInfoStackInLine]\n",
    "```\n",
    "\n",
    "\n",
    "- 可以用表格的形式嵌入。\n",
    "\n",
    "1）打开book/admin.py文件，创建PeopleInfoTabularInline类。\n",
    "\n",
    "```\n",
    "class PeopleInfoTabularInline(admin.TabularInline):\n",
    "    model = PeopleInfo #要关联的模型\n",
    "    extra = 2 #附加编辑的数量\n",
    "```\n",
    "\n",
    "2）打开book/admin.py文件，修改BookInfoAdmin类如下：\n",
    "\n",
    "```\n",
    "class BookInfoAdmin(admin.ModelAdmin):\n",
    "\n",
    "    ...\n",
    "    inlines = [PeopleInfoTabularInline]\n",
    "```\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 调整站点信息\n",
    "\n",
    "Admin站点的名称信息也是可以自定义的。\n",
    "\n",
    "未调整前如下图：\n",
    "\n",
    "\n",
    "- **admin.site.site_header**设置网站页头\n",
    "- **admin.site.site_title**设置页面标题\n",
    "- **admin.site.index_title**设置首页标语\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 上传图片\n",
    "\n",
    "Django有提供文件系统支持，在Admin站点中可以轻松上传图片。\n",
    "\n",
    "使用Admin站点保存图片，需要安装Python的图片操作包\n",
    "\n",
    "```\n",
    "pip install Pillow\n",
    "```\n",
    "\n",
    "##  配置\n",
    "\n",
    "默认情况下，Django会将上传的图片保存在本地服务器上，需要配置保存的路径。\n",
    "\n",
    "我们可以将上传的文件保存在静态文件目录中，如我们之前设置的static目录中在settings.py 文件中添加如下上传保存目录信息\n",
    "\n",
    "```\n",
    "MEDIA_ROOT=os.path.join(BASE_DIR,\"static/media\")\n",
    "```\n",
    "\n",
    "\n",
    "##  为模型类添加ImageField字段\n",
    "\n",
    "我们为之前的BookInfo模型类添加一个ImageFiled\n",
    "\n",
    "```\n",
    "class BookInfo(models.Model):\n",
    "\n",
    "    ...\n",
    "    image = models.ImageField(upload_to='book', verbose_name='图片', null=True)\n",
    "```\n",
    "\n",
    "- upload_to 选项指明该字段的图片保存在MEDIA_ROOT目录中的哪个子目录\n",
    "\n",
    "进行数据库迁移操作\n",
    "\n",
    "```\n",
    "python manage.py makemigrations\n",
    "python manage.py migrate\n",
    "```\n",
    "\n",
    "## 使用Admin站点上传图片\n",
    "\n",
    "进入Admin站点的图书管理页面，选择一个图书，能发现多出来一个上传图片的字段\n",
    "\n",
    "\n",
    "选择一张图片并保存后，图片会被保存在**static/media/book/**目录下。\n",
    "\n",
    "在数据库中，我们能看到image字段被设置为图片的路径\n",
    "\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": true,
   "toc_position": {
    "height": "calc(100% - 180px)",
    "left": "10px",
    "top": "150px",
    "width": "336px"
   },
   "toc_section_display": true,
   "toc_window_display": true
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
